/** * @param curve the curve * @param hashAlgo the JCA string for the hash algorithm * @param sc the parameter L represented as ScalarOps * @param B the parameter B * @throws IllegalArgumentException if hash algorithm is unsupported or length is wrong */ public EdDSAParameterSpec(Curve curve, String hashAlgo, ScalarOps sc, GroupElement B) { try { MessageDigest hash = MessageDigest.getInstance(hashAlgo); // EdDSA hash function must produce 2b-bit output if (curve.getField().getb()/4 != hash.getDigestLength()) throw new IllegalArgumentException("Hash output is not 2b-bit"); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException("Unsupported hash algorithm"); } this.curve = curve; this.hashAlgo = hashAlgo; this.sc = sc; this.B = B; }
/** * @since 0.9.25 */ @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Curve)) return false; Curve c = (Curve) o; return f.equals(c.getField()) && d.equals(c.getD()) && I.equals(c.getI()); } }
/** * @param pk the public key * @param spec the parameter specification for this key * @throws IllegalArgumentException if key length is wrong */ public EdDSAPublicKeySpec(byte[] pk, EdDSAParameterSpec spec) { if (pk.length != spec.getCurve().getField().getb()/8) throw new IllegalArgumentException("public-key length is wrong"); this.A = new GroupElement(spec.getCurve(), pk); this.spec = spec; }
private void digestInitSign(EdDSAPrivateKey privKey) { // Preparing for hash // r = H(h_b,...,h_2b-1,M) int b = privKey.getParams().getCurve().getField().getb(); digest.update(privKey.getH(), b/8, b/4 - b/8); }
/** * @param seed the private key * @param spec the parameter specification for this key * @throws IllegalArgumentException if seed length is wrong or hash algorithm is unsupported */ public EdDSAPrivateKeySpec(byte[] seed, EdDSAParameterSpec spec) { int bd8 = spec.getCurve().getField().getb() / 8; if (seed.length != bd8) throw new IllegalArgumentException("seed length is wrong"); this.spec = spec; this.seed = seed; try { MessageDigest hash = MessageDigest.getInstance(spec.getHashAlgorithm()); // 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[bd8 - 1] &= 63; h[bd8 - 1] |= 64; a = Arrays.copyOfRange(h, 0, bd8); 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 spec the parameter specification for this key * @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) { int bd4 = spec.getCurve().getField().getb() / 4; if (h.length != bd4) throw new IllegalArgumentException("hash length is wrong"); int bd8 = bd4 / 2; this.seed = null; this.h = h; this.spec = spec; h[0] &= 248; h[bd8 - 1] &= 63; h[bd8 - 1] |= 64; a = Arrays.copyOfRange(h, 0, bd8); A = spec.getB().scalarMultiply(a); }
@Test public void addingNeutralGroupElementDoesNotChangeGroupElement() { final GroupElement neutral = GroupElement.p3(curve, curve.getField().ZERO, curve.getField().ONE, curve.getField().ONE, curve.getField().ZERO); for (int i=0; i<1000; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); // Act: final GroupElement h1 = g.add(neutral.toCached()); final GroupElement h2 = neutral.add(g.toCached()); // Assert: Assert.assertThat(g, IsEqual.equalTo(h1)); Assert.assertThat(g, IsEqual.equalTo(h2)); } }
int b = curve.getField().getb(); ByteBuffer out = ByteBuffer.allocate(b/4); out.put(Rbyte).put(S);
public KeyPair generateKeyPair() { if (!initialized) initialize(DEFAULT_KEYSIZE, RandomSource.getInstance()); byte[] seed = new byte[edParams.getCurve().getField().getb()/8]; random.nextBytes(seed); EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(seed, edParams); EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(privKey.getA(), edParams); return new KeyPair(new EdDSAPublicKey(pubKey), new EdDSAPrivateKey(privKey)); }
@Test public void testb() { int b = curve.getField().getb(); assertThat(b, is(greaterThanOrEqualTo(10))); try { MessageDigest h = MessageDigest.getInstance(ed25519.getHashAlgorithm()); assertThat(8 * h.getDigestLength(), is(equalTo(2 * b))); } catch (NoSuchAlgorithmException e) { fail(e.getMessage()); } }
private boolean x_engineVerify(byte[] sigBytes) throws SignatureException { Curve curve = key.getParams().getCurve(); int b = curve.getField().getb(); if (sigBytes.length != b/4) throw new SignatureException("signature length is wrong");
public static GroupElement[] getDoublePrecomputation(String fileName) { EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); Curve curve = ed25519.getCurve(); Field field = curve.getField(); GroupElement[] dblPrecmp = new GroupElement[8]; BufferedReader file = null;
public static GroupElement[][] getPrecomputation(String fileName) { EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519); Curve curve = ed25519.getCurve(); Field field = curve.getField(); GroupElement[][] precmp = new GroupElement[32][8]; BufferedReader file = null;
@Test public void scalarMultiplyBasePointWithOneReturnsBasePoint() { // Arrange: final GroupElement basePoint = ed25519.getB(); // Act: final GroupElement g = basePoint.scalarMultiply(curve.getField().ONE.toByteArray()); // Assert: Assert.assertThat(basePoint, IsEqual.equalTo(g)); }
@Test public void scalarMultiplyBasePointWithZeroReturnsNeutralElement() { // Arrange: final GroupElement basePoint = ed25519.getB(); // Act: final GroupElement g = basePoint.scalarMultiply(curve.getField().ZERO.toByteArray()); // Assert: Assert.assertThat(curve.getZero(GroupElement.Representation.P3), IsEqual.equalTo(g)); }
/** * Verify that a point is on the curve. * @param curve The curve to check. * @return true if the point lies on the curve. */ public boolean isOnCurve(Curve curve) { switch (repr) { case P2: case P3: FieldElement recip = Z.invert(); FieldElement x = X.multiply(recip); FieldElement y = Y.multiply(recip); FieldElement xx = x.square(); FieldElement yy = y.square(); FieldElement dxxyy = curve.getD().multiply(xx).multiply(yy); return curve.getField().ONE.add(dxxyy).add(xx).equals(yy); default: return toP2().isOnCurve(curve); } }
@Test public void mathUtilsWorkAsExpected() { final GroupElement neutral = GroupElement.p3(curve, curve.getField().ZERO, curve.getField().ONE, curve.getField().ONE, curve.getField().ZERO); for (int i=0; i<1000; i++) { final GroupElement g = getRandomGroupElement(); final GroupElement h = MathUtils.scalarMultiplyGroupElement(g, curve.getField().ZERO);
@Test public void isOnCurveReturnsFalseForPointsNotOnTheCurve() { for (int i=0; i<100; i++) { // Arrange: final GroupElement g = MathUtils.getRandomGroupElement(); final GroupElement h = GroupElement.p2(curve, g.getX(), g.getY(), g.getZ().multiply(curve.getField().TWO)); // Assert (can only fail for 5*Z^2=1): Assert.assertThat(h.isOnCurve(), IsEqual.equalTo(false)); } } }
private static NKey createPair(Type type, SecureRandom random) throws IOException, NoSuchProviderException, NoSuchAlgorithmException { if (random == null) { random = SecureRandom.getInstance("SHA1PRNG", "SUN"); } byte[] seed = new byte[NKey.ed25519.getCurve().getField().getb() / 8]; random.nextBytes(seed); return createPair(type, seed); }