package net.i2p.crypto.eddsa.spec; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.spec.KeySpec; import java.util.Arrays; import net.i2p.crypto.eddsa.math.GroupElement; /** * * @since 0.9.15 * @author str4d * */ public class EdDSAPrivateKeySpec implements KeySpec { private final byte[] seed; private final byte[] h; private final byte[] a; private final GroupElement A; private final EdDSAParameterSpec spec; /** * @throws IllegalArgumentException if seed length is wrong or hash algorithm is unsupported */ public EdDSAPrivateKeySpec(byte[] seed, EdDSAParameterSpec spec) { if (seed.length != spec.getCurve().getField().getb()/8) throw new IllegalArgumentException("seed length is wrong"); this.spec = spec; this.seed = seed; try { MessageDigest hash = MessageDigest.getInstance(spec.getHashAlgorithm()); int b = spec.getCurve().getField().getb(); // H(k) h = hash.digest(seed); /*a = BigInteger.valueOf(2).pow(b-2); for (int i=3;i<(b-2);i++) { a = a.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(Utils.bit(h,i)))); }*/ // Saves ~0.4ms per key when running signing tests. // TODO: are these bitflips the same for any hash function? h[0] &= 248; h[(b/8)-1] &= 63; h[(b/8)-1] |= 64; a = Arrays.copyOfRange(h, 0, b/8); A = spec.getB().scalarMultiply(a); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException("Unsupported hash algorithm"); } } /** * Initialize directly from the hash. * getSeed() will return null if this constructor is used. * * @param h the private key * @throws IllegalArgumentException if hash length is wrong * @since 0.9.27 (GitHub issue #17) */ public EdDSAPrivateKeySpec(EdDSAParameterSpec spec, byte[] h) { if (h.length != spec.getCurve().getField().getb()/4) throw new IllegalArgumentException("hash length is wrong"); this.seed = null; this.h = h; this.spec = spec; int b = spec.getCurve().getField().getb(); h[0] &= 248; h[(b/8)-1] &= 63; h[(b/8)-1] |= 64; a = Arrays.copyOfRange(h, 0, b/8); A = spec.getB().scalarMultiply(a); } public EdDSAPrivateKeySpec(byte[] seed, byte[] h, byte[] a, GroupElement A, EdDSAParameterSpec spec) { this.seed = seed; this.h = h; this.a = a; this.A = A; this.spec = spec; } /** * @return will be null if constructed directly from the private key */ public byte[] getSeed() { return seed; } /** * @return the hash */ public byte[] getH() { return h; } /** * @return the private key */ public byte[] geta() { return a; } /** * @return the public key */ public GroupElement getA() { return A; } public EdDSAParameterSpec getParams() { return spec; } }