forked from I2P_Developers/i2p.i2p
EdDSA: Backport versions 0.2/0.3 from github:
- Change key encoding to match curdle draft - Support key decoding based on curdle draft - Implement true constant-time cmov() - Add handling of X509Key-wrapped EdDSA keys (GitHub PR #47) - Clarify that KeyPairGenerator takes a key size, not strength - Javadocs GitHub PR #58: - Make GroupElement immutable by moving the pre-computed logic to the constructors, allowing the synchronized checking of whether the pre-computed logic had executed or not to be removed since it always has when it is used because those code paths are modified to request it at construction time. - This allows getNegativeA() to be lazy, and doesn't need volatile due to the immutability (and final fields - this is important part of the contract with the JVM memory model). - Remove synchronized contention from the named curve table get method. - Generally remove use of the named curve table get method with a constant curve name in hot code paths in favour of using a new static constant for the curve spec. Overall performance changes: - Keygen 46% faster - Signing 39% slower (due to cmov) - Verify 2% faster
This commit is contained in:
@ -11,6 +11,8 @@ import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.crypto.eddsa.math.Curve;
|
||||
@ -54,6 +56,8 @@ import net.i2p.crypto.eddsa.math.ScalarOps;
|
||||
*
|
||||
*/
|
||||
public final class EdDSAEngine extends Signature {
|
||||
public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA";
|
||||
|
||||
private MessageDigest digest;
|
||||
private ByteArrayOutputStream baos;
|
||||
private EdDSAKey key;
|
||||
@ -76,14 +80,14 @@ public final class EdDSAEngine extends Signature {
|
||||
private static class OneShotSpec implements AlgorithmParameterSpec {}
|
||||
|
||||
/**
|
||||
* No specific hash requested, allows any EdDSA key.
|
||||
* No specific EdDSA-internal hash requested, allows any EdDSA key.
|
||||
*/
|
||||
public EdDSAEngine() {
|
||||
super("EdDSA");
|
||||
super(SIGNATURE_ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific hash requested, only matching keys will be allowed.
|
||||
* Specific EdDSA-internal hash requested, only matching keys will be allowed.
|
||||
* @param digest the hash algorithm that keys must have to sign or verify.
|
||||
*/
|
||||
public EdDSAEngine(MessageDigest digest) {
|
||||
@ -147,6 +151,16 @@ public final class EdDSAEngine extends Signature {
|
||||
}
|
||||
} else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
|
||||
throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
|
||||
} else if (publicKey.getClass().getName().equals("sun.security.x509.X509Key")) {
|
||||
// X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
|
||||
// key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
|
||||
EdDSAPublicKey parsedPublicKey;
|
||||
try {
|
||||
parsedPublicKey = new EdDSAPublicKey(new X509EncodedKeySpec(publicKey.getEncoded()));
|
||||
} catch (InvalidKeySpecException ex) {
|
||||
throw new InvalidKeyException("cannot handle X.509 EdDSA public key: " + publicKey.getAlgorithm());
|
||||
}
|
||||
engineInitVerify(parsedPublicKey);
|
||||
} else {
|
||||
throw new InvalidKeyException("cannot identify EdDSA public key: " + publicKey.getClass());
|
||||
}
|
||||
@ -311,6 +325,8 @@ public final class EdDSAEngine extends Signature {
|
||||
* sig = sign()
|
||||
*</pre>
|
||||
*
|
||||
* @param data the message to be signed
|
||||
* @return the signature
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
@ -330,6 +346,10 @@ public final class EdDSAEngine extends Signature {
|
||||
* sig = sign()
|
||||
*</pre>
|
||||
*
|
||||
* @param data byte array containing the message to be signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @return the signature
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
@ -351,6 +371,9 @@ public final class EdDSAEngine extends Signature {
|
||||
* ok = verify(signature)
|
||||
*</pre>
|
||||
*
|
||||
* @param data the message that was signed
|
||||
* @param signature of the message
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
@ -370,6 +393,11 @@ public final class EdDSAEngine extends Signature {
|
||||
* ok = verify(signature)
|
||||
*</pre>
|
||||
*
|
||||
* @param data byte array containing the message that was signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @param signature of the message
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
@ -389,6 +417,11 @@ public final class EdDSAEngine extends Signature {
|
||||
* ok = verify(signature, sigoff, siglen)
|
||||
*</pre>
|
||||
*
|
||||
* @param data the message that was signed
|
||||
* @param signature byte array containing the signature
|
||||
* @param sigoff the start of the signature
|
||||
* @param siglen the length of the signature
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
@ -408,6 +441,13 @@ public final class EdDSAEngine extends Signature {
|
||||
* ok = verify(signature, sigoff, siglen)
|
||||
*</pre>
|
||||
*
|
||||
* @param data byte array containing the message that was signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @param signature byte array containing the signature
|
||||
* @param sigoff the start of the signature
|
||||
* @param siglen the length of the signature
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
* @since 0.9.25
|
||||
|
@ -11,8 +11,14 @@ import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
*/
|
||||
public interface EdDSAKey {
|
||||
/**
|
||||
* return a parameter specification representing the EdDSA domain
|
||||
* parameters for the key.
|
||||
* The reported key algorithm for all EdDSA keys
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public String KEY_ALGORITHM = "EdDSA";
|
||||
|
||||
/**
|
||||
* @return a parameter specification representing the EdDSA domain
|
||||
* parameters for the key.
|
||||
*/
|
||||
public EdDSAParameterSpec getParams();
|
||||
}
|
||||
|
@ -13,11 +13,15 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
/**
|
||||
* An EdDSA private key.
|
||||
*<p>
|
||||
* Warning: Private key encoding is not fully specified in the
|
||||
* current IETF draft. This implementation uses PKCS#8 encoding,
|
||||
* Warning: Private key encoding is based on the current curdle WG draft,
|
||||
* and is subject to change. See getEncoded().
|
||||
*</p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
* For compatibility with older releases, decoding supports both the old and new
|
||||
* draft specifications. See decode().
|
||||
*</p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
*</p><p>
|
||||
* Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
*</p>
|
||||
*
|
||||
* @since 0.9.15
|
||||
@ -33,6 +37,12 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
|
||||
private final byte[] Abyte;
|
||||
private final EdDSAParameterSpec edDsaSpec;
|
||||
|
||||
// OID 1.3.101.xxx
|
||||
private static final int OID_OLD = 100;
|
||||
private static final int OID_ED25519 = 112;
|
||||
private static final int OID_BYTE = 11;
|
||||
private static final int IDLEN_BYTE = 6;
|
||||
|
||||
public EdDSAPrivateKey(EdDSAPrivateKeySpec spec) {
|
||||
this.seed = spec.getSeed();
|
||||
this.h = spec.getH();
|
||||
@ -47,121 +57,213 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
|
||||
*/
|
||||
public EdDSAPrivateKey(PKCS8EncodedKeySpec spec) throws InvalidKeySpecException {
|
||||
this(new EdDSAPrivateKeySpec(decode(spec.getEncoded()),
|
||||
EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)));
|
||||
EdDSANamedCurveTable.ED_25519_CURVE_SPEC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "EdDSA";
|
||||
return KEY_ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "PKCS#8";
|
||||
}
|
||||
|
||||
/**
|
||||
* This follows the docs from
|
||||
* java.security.spec.PKCS8EncodedKeySpec
|
||||
* quote:
|
||||
* Returns the public key in its canonical encoding.
|
||||
*<p>
|
||||
* This implements the following specs:
|
||||
*<ul><li>
|
||||
* General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
*</li><li>
|
||||
* Key encoding: https://tools.ietf.org/html/rfc8032
|
||||
*</li></ul>
|
||||
*<p>
|
||||
* This encodes the seed. It will return null if constructed from
|
||||
* a spec which was directly constructed from H, in which case seed is null.
|
||||
*</p><p>
|
||||
* For keys in older formats, decoding and then re-encoding is sufficient to
|
||||
* migrate them to the canonical encoding.
|
||||
*</p>
|
||||
* Relevant spec quotes:
|
||||
*<pre>
|
||||
* The PrivateKeyInfo syntax is defined in the PKCS#8 standard as follows:
|
||||
* PrivateKeyInfo ::= SEQUENCE {
|
||||
* OneAsymmetricKey ::= SEQUENCE {
|
||||
* version Version,
|
||||
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
* privateKey PrivateKey,
|
||||
* attributes [0] IMPLICIT Attributes OPTIONAL }
|
||||
* attributes [0] Attributes OPTIONAL,
|
||||
* ...,
|
||||
* [[2: publicKey [1] PublicKey OPTIONAL ]],
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* Version ::= INTEGER
|
||||
* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
* PrivateKey ::= OCTET STRING
|
||||
* PublicKey ::= OCTET STRING
|
||||
* Attributes ::= SET OF Attribute
|
||||
*</pre>
|
||||
*
|
||||
*<pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE
|
||||
* {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY OPTIONAL
|
||||
* }
|
||||
* ... when encoding a OneAsymmetricKey object, the private key is wrapped
|
||||
* in a CurvePrivateKey object and wrapped by the OCTET STRING of the
|
||||
* 'privateKey' field.
|
||||
*
|
||||
* CurvePrivateKey ::= OCTET STRING
|
||||
*</pre>
|
||||
*
|
||||
* Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
*<pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
* }
|
||||
*
|
||||
* Note that the private key encoding is not fully specified in the Josefsson draft version 04,
|
||||
* and the example could be wrong, as it's lacking Version and AlgorithmIdentifier.
|
||||
* This will hopefully be clarified in the next draft.
|
||||
* But sun.security.pkcs.PKCS8Key expects them so we must include them for keytool to work.
|
||||
* For all of the OIDs, the parameters MUST be absent.
|
||||
*</pre>
|
||||
*
|
||||
* This encodes the seed. It will return null if constructed from
|
||||
* a spec which was directly constructed from H, in which case seed is null.
|
||||
*<pre>
|
||||
* id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
*</pre>
|
||||
*
|
||||
* @return 49 bytes for Ed25519, null for other curves
|
||||
* @return 48 bytes for Ed25519, null for other curves
|
||||
* @since implemented in 0.9.25
|
||||
*/
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)))
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.ED_25519_CURVE_SPEC))
|
||||
return null;
|
||||
int totlen = 17 + seed.length;
|
||||
if (seed == null)
|
||||
return null;
|
||||
int totlen = 16 + seed.length;
|
||||
byte[] rv = new byte[totlen];
|
||||
int idx = 0;
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = (byte) (15 + seed.length);
|
||||
|
||||
rv[idx++] = (byte) (totlen - 2);
|
||||
// version
|
||||
// not in the Josefsson example
|
||||
rv[idx++] = 0x02;
|
||||
rv[idx++] = 1;
|
||||
// v1 - no public key included
|
||||
rv[idx++] = 0;
|
||||
|
||||
// Algorithm Identifier
|
||||
// sequence
|
||||
// not in the Josefsson example
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = 8;
|
||||
// OID 1.3.101.100
|
||||
rv[idx++] = 5;
|
||||
// OID
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
|
||||
// not in the Josefsson example
|
||||
rv[idx++] = 0x06;
|
||||
rv[idx++] = 3;
|
||||
rv[idx++] = (1 * 40) + 3;
|
||||
rv[idx++] = 101;
|
||||
rv[idx++] = 100;
|
||||
// params
|
||||
rv[idx++] = 0x0a;
|
||||
rv[idx++] = 1;
|
||||
rv[idx++] = 1; // Ed25519
|
||||
// the key
|
||||
rv[idx++] = (byte) OID_ED25519;
|
||||
// params - absent
|
||||
// PrivateKey
|
||||
rv[idx++] = 0x04; // octet string
|
||||
rv[idx++] = (byte) (2 + seed.length);
|
||||
// CurvePrivateKey
|
||||
rv[idx++] = 0x04; // octet string
|
||||
rv[idx++] = (byte) seed.length;
|
||||
// the key
|
||||
System.arraycopy(seed, 0, rv, idx, seed.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is really dumb for now.
|
||||
* See getEncoded().
|
||||
* Extracts the private key bytes from the provided encoding.
|
||||
*<p>
|
||||
* This will decode data conforming to the current spec at
|
||||
* https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* or as inferred from the old spec at
|
||||
* https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
|
||||
*</p><p>
|
||||
* Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
|
||||
* of NULL, as it is required for interoperability with the default Java
|
||||
* keystore. Other implementations MUST NOT copy this behaviour from here
|
||||
* unless they also need to read keys from the default Java keystore.
|
||||
*</p><p>
|
||||
* This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
|
||||
* See also getEncoded().
|
||||
*
|
||||
* @return 32 bytes for Ed25519, throws for other curves
|
||||
* @since 0.9.25
|
||||
*/
|
||||
private static byte[] decode(byte[] d) throws InvalidKeySpecException {
|
||||
try {
|
||||
//
|
||||
// Setup and OID check
|
||||
//
|
||||
int totlen = 48;
|
||||
int idlen = 5;
|
||||
int doid = d[OID_BYTE];
|
||||
if (doid == OID_OLD) {
|
||||
totlen = 49;
|
||||
idlen = 8;
|
||||
} else if (doid == OID_ED25519) {
|
||||
// Detect parameter value of NULL
|
||||
if (d[IDLEN_BYTE] == 7) {
|
||||
totlen = 50;
|
||||
idlen = 7;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
|
||||
//
|
||||
// Pre-decoding check
|
||||
//
|
||||
if (d.length != totlen) {
|
||||
throw new InvalidKeySpecException("invalid key spec length");
|
||||
}
|
||||
|
||||
//
|
||||
// Decoding
|
||||
//
|
||||
int idx = 0;
|
||||
if (d[idx++] != 0x30 ||
|
||||
d[idx++] != 47 ||
|
||||
d[idx++] != (totlen - 2) ||
|
||||
d[idx++] != 0x02 ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 0 ||
|
||||
d[idx++] != 0x30 ||
|
||||
d[idx++] != 8 ||
|
||||
d[idx++] != idlen ||
|
||||
d[idx++] != 0x06 ||
|
||||
d[idx++] != 3 ||
|
||||
d[idx++] != (1 * 40) + 3 ||
|
||||
d[idx++] != 101 ||
|
||||
d[idx++] != 100 ||
|
||||
d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 0x04 ||
|
||||
d[idx++] != 101) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
idx++; // OID, checked above
|
||||
// parameters only with old OID
|
||||
if (doid == OID_OLD) {
|
||||
if (d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
} else {
|
||||
// Handle parameter value of NULL
|
||||
//
|
||||
// Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
// Regardless of the defect in the original 1997 syntax,
|
||||
// implementations MUST NOT accept a parameters value of NULL.
|
||||
//
|
||||
// But Java's default keystore puts it in (when decoding as
|
||||
// PKCS8 and then re-encoding to pass on), so we must accept it.
|
||||
if (idlen == 7) {
|
||||
if (d[idx++] != 0x05 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
// PrivateKey wrapping the CurvePrivateKey
|
||||
if (d[idx++] != 0x04 ||
|
||||
d[idx++] != 34) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
if (d[idx++] != 0x04 ||
|
||||
d[idx++] != 32) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
@ -173,6 +275,7 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdDSAParameterSpec getParams() {
|
||||
return edDsaSpec;
|
||||
}
|
||||
|
@ -13,10 +13,15 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
/**
|
||||
* An EdDSA public key.
|
||||
*<p>
|
||||
* Warning: Public key encoding is is based on the
|
||||
* current IETF draft, and is subject to change. See getEncoded().
|
||||
* Warning: Public key encoding is is based on the current curdle WG draft,
|
||||
* and is subject to change. See getEncoded().
|
||||
*</p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
* For compatibility with older releases, decoding supports both the old and new
|
||||
* draft specifications. See decode().
|
||||
*</p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
*</p><p>
|
||||
* Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
*</p>
|
||||
*
|
||||
* @since 0.9.15
|
||||
@ -26,13 +31,18 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
private static final long serialVersionUID = 9837459837498475L;
|
||||
private final GroupElement A;
|
||||
private final GroupElement Aneg;
|
||||
private GroupElement Aneg;
|
||||
private final byte[] Abyte;
|
||||
private final EdDSAParameterSpec edDsaSpec;
|
||||
|
||||
// OID 1.3.101.xxx
|
||||
private static final int OID_OLD = 100;
|
||||
private static final int OID_ED25519 = 112;
|
||||
private static final int OID_BYTE = 8;
|
||||
private static final int IDLEN_BYTE = 3;
|
||||
|
||||
public EdDSAPublicKey(EdDSAPublicKeySpec spec) {
|
||||
this.A = spec.getA();
|
||||
this.Aneg = spec.getNegativeA();
|
||||
this.Abyte = this.A.toByteArray();
|
||||
this.edDsaSpec = spec.getParams();
|
||||
}
|
||||
@ -42,65 +52,81 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
*/
|
||||
public EdDSAPublicKey(X509EncodedKeySpec spec) throws InvalidKeySpecException {
|
||||
this(new EdDSAPublicKeySpec(decode(spec.getEncoded()),
|
||||
EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)));
|
||||
EdDSANamedCurveTable.ED_25519_CURVE_SPEC));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "EdDSA";
|
||||
return KEY_ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "X.509";
|
||||
}
|
||||
|
||||
/**
|
||||
* This follows the spec at
|
||||
* ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
* which matches the docs from
|
||||
* java.security.spec.X509EncodedKeySpec
|
||||
* quote:
|
||||
* Returns the public key in its canonical encoding.
|
||||
*<p>
|
||||
* This implements the following specs:
|
||||
*<ul><li>
|
||||
* General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
*</li><li>
|
||||
* Key encoding: https://tools.ietf.org/html/rfc8032
|
||||
*</li></ul>
|
||||
*<p>
|
||||
* For keys in older formats, decoding and then re-encoding is sufficient to
|
||||
* migrate them to the canonical encoding.
|
||||
*</p>
|
||||
* Relevant spec quotes:
|
||||
*<pre>
|
||||
* The SubjectPublicKeyInfo syntax is defined in the X.509 standard as follows:
|
||||
* SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
* algorithm AlgorithmIdentifier,
|
||||
* subjectPublicKey BIT STRING }
|
||||
*</pre>
|
||||
* In the X.509 certificate, the subjectPublicKeyInfo field has the
|
||||
* SubjectPublicKeyInfo type, which has the following ASN.1 syntax:
|
||||
*
|
||||
*<pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE
|
||||
* {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY OPTIONAL
|
||||
* SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
* algorithm AlgorithmIdentifier,
|
||||
* subjectPublicKey BIT STRING
|
||||
* }
|
||||
*</pre>
|
||||
*
|
||||
* @return 47 bytes for Ed25519, null for other curves
|
||||
*<pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
* }
|
||||
*
|
||||
* For all of the OIDs, the parameters MUST be absent.
|
||||
*</pre>
|
||||
*
|
||||
*<pre>
|
||||
* id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
*</pre>
|
||||
*
|
||||
* @return 44 bytes for Ed25519, null for other curves
|
||||
* @since implemented in 0.9.25
|
||||
*/
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)))
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.ED_25519_CURVE_SPEC))
|
||||
return null;
|
||||
int totlen = 15 + Abyte.length;
|
||||
int totlen = 12 + Abyte.length;
|
||||
byte[] rv = new byte[totlen];
|
||||
int idx = 0;
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = (byte) (13 + Abyte.length);
|
||||
rv[idx++] = (byte) (totlen - 2);
|
||||
// Algorithm Identifier
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = 8;
|
||||
// OID 1.3.101.100
|
||||
rv[idx++] = 5;
|
||||
// OID
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
|
||||
rv[idx++] = 0x06;
|
||||
rv[idx++] = 3;
|
||||
rv[idx++] = (1 * 40) + 3;
|
||||
rv[idx++] = 101;
|
||||
rv[idx++] = 100;
|
||||
// params
|
||||
rv[idx++] = 0x0a;
|
||||
rv[idx++] = 1;
|
||||
rv[idx++] = 1; // Ed25519
|
||||
rv[idx++] = (byte) OID_ED25519;
|
||||
// params - absent
|
||||
// the key
|
||||
rv[idx++] = 0x03; // bit string
|
||||
rv[idx++] = (byte) (1 + Abyte.length);
|
||||
@ -110,28 +136,93 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is really dumb for now.
|
||||
* See getEncoded().
|
||||
* Extracts the public key bytes from the provided encoding.
|
||||
*<p>
|
||||
* This will decode data conforming to the current spec at
|
||||
* https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* or the old spec at
|
||||
* https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
|
||||
*</p><p>
|
||||
* Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
|
||||
* of NULL, as it is required for interoperability with the default Java
|
||||
* keystore. Other implementations MUST NOT copy this behaviour from here
|
||||
* unless they also need to read keys from the default Java keystore.
|
||||
*</p><p>
|
||||
* This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
|
||||
* See also getEncoded().
|
||||
*</p>
|
||||
*
|
||||
* @return 32 bytes for Ed25519, throws for other curves
|
||||
* @since 0.9.25
|
||||
*/
|
||||
private static byte[] decode(byte[] d) throws InvalidKeySpecException {
|
||||
try {
|
||||
//
|
||||
// Setup and OID check
|
||||
//
|
||||
int totlen = 44;
|
||||
int idlen = 5;
|
||||
int doid = d[OID_BYTE];
|
||||
if (doid == OID_OLD) {
|
||||
totlen = 47;
|
||||
idlen = 8;
|
||||
} else if (doid == OID_ED25519) {
|
||||
// Detect parameter value of NULL
|
||||
if (d[IDLEN_BYTE] == 7) {
|
||||
totlen = 46;
|
||||
idlen = 7;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
|
||||
//
|
||||
// Pre-decoding check
|
||||
//
|
||||
if (d.length != totlen) {
|
||||
throw new InvalidKeySpecException("invalid key spec length");
|
||||
}
|
||||
|
||||
//
|
||||
// Decoding
|
||||
//
|
||||
int idx = 0;
|
||||
if (d[idx++] != 0x30 ||
|
||||
d[idx++] != 45 ||
|
||||
d[idx++] != (totlen - 2) ||
|
||||
d[idx++] != 0x30 ||
|
||||
d[idx++] != 8 ||
|
||||
d[idx++] != idlen ||
|
||||
d[idx++] != 0x06 ||
|
||||
d[idx++] != 3 ||
|
||||
d[idx++] != (1 * 40) + 3 ||
|
||||
d[idx++] != 101 ||
|
||||
d[idx++] != 100 ||
|
||||
d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 0x03 ||
|
||||
d[idx++] != 101) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
idx++; // OID, checked above
|
||||
// parameters only with old OID
|
||||
if (doid == OID_OLD) {
|
||||
if (d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
} else {
|
||||
// Handle parameter value of NULL
|
||||
//
|
||||
// Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
// Regardless of the defect in the original 1997 syntax,
|
||||
// implementations MUST NOT accept a parameters value of NULL.
|
||||
//
|
||||
// But Java's default keystore puts it in (when decoding as
|
||||
// PKCS8 and then re-encoding to pass on), so we must accept it.
|
||||
if (idlen == 7) {
|
||||
if (d[idx++] != 0x05 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d[idx++] != 0x03 ||
|
||||
d[idx++] != 33 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
@ -144,6 +235,7 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdDSAParameterSpec getParams() {
|
||||
return edDsaSpec;
|
||||
}
|
||||
@ -153,7 +245,13 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
}
|
||||
|
||||
public GroupElement getNegativeA() {
|
||||
return Aneg;
|
||||
// Only read Aneg once, otherwise read re-ordering might occur between here and return. Requires all GroupElement's fields to be final.
|
||||
GroupElement ourAneg = Aneg;
|
||||
if(ourAneg == null) {
|
||||
ourAneg = A.negate();
|
||||
Aneg = ourAneg;
|
||||
}
|
||||
return ourAneg;
|
||||
}
|
||||
|
||||
public byte[] getAbyte() {
|
||||
|
@ -17,12 +17,12 @@ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
* Default strength is 256
|
||||
* Default keysize is 256 (Ed25519)
|
||||
*
|
||||
* @since 0.9.15
|
||||
*/
|
||||
public final class KeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
private static final int DEFAULT_STRENGTH = 256;
|
||||
private static final int DEFAULT_KEYSIZE = 256;
|
||||
private EdDSAParameterSpec edParams;
|
||||
private SecureRandom random;
|
||||
private boolean initialized;
|
||||
@ -32,11 +32,11 @@ public final class KeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
static {
|
||||
edParameters = new Hashtable<Integer, AlgorithmParameterSpec>();
|
||||
|
||||
edParameters.put(Integer.valueOf(DEFAULT_STRENGTH), new EdDSAGenParameterSpec(EdDSANamedCurveTable.CURVE_ED25519_SHA512));
|
||||
edParameters.put(Integer.valueOf(DEFAULT_KEYSIZE), new EdDSAGenParameterSpec(EdDSANamedCurveTable.ED_25519));
|
||||
}
|
||||
|
||||
public void initialize(int strength, SecureRandom random) {
|
||||
AlgorithmParameterSpec edParams = edParameters.get(Integer.valueOf(strength));
|
||||
public void initialize(int keysize, SecureRandom random) {
|
||||
AlgorithmParameterSpec edParams = edParameters.get(Integer.valueOf(keysize));
|
||||
if (edParams == null)
|
||||
throw new InvalidParameterException("unknown key type.");
|
||||
try {
|
||||
@ -61,7 +61,7 @@ public final class KeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
public KeyPair generateKeyPair() {
|
||||
if (!initialized)
|
||||
initialize(DEFAULT_STRENGTH, RandomSource.getInstance());
|
||||
initialize(DEFAULT_KEYSIZE, RandomSource.getInstance());
|
||||
|
||||
byte[] seed = new byte[edParams.getCurve().getField().getb()/8];
|
||||
random.nextBytes(seed);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.i2p.crypto.eddsa;
|
||||
|
||||
/**
|
||||
* Basic utilities for eddsa.
|
||||
* Basic utilities for EdDSA.
|
||||
* Not for external use, not maintained as a public API.
|
||||
*
|
||||
* @since 0.9.15
|
||||
@ -11,6 +11,8 @@ package net.i2p.crypto.eddsa;
|
||||
public class Utils {
|
||||
/**
|
||||
* Constant-time byte comparison.
|
||||
* @param b a byte
|
||||
* @param c a byte
|
||||
* @return 1 if b and c are equal, 0 otherwise.
|
||||
*/
|
||||
public static int equal(int b, int c) {
|
||||
@ -24,6 +26,8 @@ public class Utils {
|
||||
|
||||
/**
|
||||
* Constant-time byte[] comparison.
|
||||
* @param b a byte[]
|
||||
* @param c a byte[]
|
||||
* @return 1 if b and c are equal, 0 otherwise.
|
||||
*/
|
||||
public static int equal(byte[] b, byte[] c) {
|
||||
|
@ -19,6 +19,7 @@ public class Curve implements Serializable {
|
||||
|
||||
private final GroupElement zeroP2;
|
||||
private final GroupElement zeroP3;
|
||||
private final GroupElement zeroP3PrecomputedDouble;
|
||||
private final GroupElement zeroPrecomp;
|
||||
|
||||
public Curve(Field f, byte[] d, FieldElement I) {
|
||||
@ -30,7 +31,8 @@ public class Curve implements Serializable {
|
||||
FieldElement zero = f.ZERO;
|
||||
FieldElement one = f.ONE;
|
||||
zeroP2 = GroupElement.p2(this, zero, one, one);
|
||||
zeroP3 = GroupElement.p3(this, zero, one, one, zero);
|
||||
zeroP3 = GroupElement.p3(this, zero, one, one, zero, false);
|
||||
zeroP3PrecomputedDouble = GroupElement.p3(this, zero, one, one, zero, true);
|
||||
zeroPrecomp = GroupElement.precomp(this, one, one, zero);
|
||||
}
|
||||
|
||||
@ -56,6 +58,8 @@ public class Curve implements Serializable {
|
||||
return zeroP2;
|
||||
case P3:
|
||||
return zeroP3;
|
||||
case P3PrecomputedDouble:
|
||||
return zeroP3PrecomputedDouble;
|
||||
case PRECOMP:
|
||||
return zeroPrecomp;
|
||||
default:
|
||||
@ -64,9 +68,7 @@ public class Curve implements Serializable {
|
||||
}
|
||||
|
||||
public GroupElement createPoint(byte[] P, boolean precompute) {
|
||||
GroupElement ge = new GroupElement(this, P);
|
||||
if (precompute)
|
||||
ge.precompute(true);
|
||||
GroupElement ge = new GroupElement(this, P, precompute);
|
||||
return ge;
|
||||
}
|
||||
|
||||
|
@ -63,5 +63,10 @@ public abstract class FieldElement implements Serializable {
|
||||
|
||||
public abstract FieldElement pow22523();
|
||||
|
||||
/**
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public abstract FieldElement cmov(FieldElement val, final int b);
|
||||
|
||||
// Note: concrete subclasses must implement hashCode() and equals()
|
||||
}
|
||||
|
@ -40,6 +40,11 @@ public class GroupElement implements Serializable {
|
||||
P2,
|
||||
/** Extended (P^3): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT */
|
||||
P3,
|
||||
/**
|
||||
* Can only be requested. Results in P3 representation but also populates dblPrecmp.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
P3PrecomputedDouble,
|
||||
/** Completed (P x P): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T */
|
||||
P1P1,
|
||||
/** Precomputed (Duif): (y+x,y-x,2dxy) */
|
||||
@ -81,7 +86,29 @@ public class GroupElement implements Serializable {
|
||||
final FieldElement Y,
|
||||
final FieldElement Z,
|
||||
final FieldElement T) {
|
||||
return new GroupElement(curve, Representation.P3, X, Y, Z, T);
|
||||
return p3(curve, X, Y, Z, T, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new group element in P3 representation, potentially with pre-computation.
|
||||
*
|
||||
* @param curve The curve.
|
||||
* @param X The X coordinate.
|
||||
* @param Y The Y coordinate.
|
||||
* @param Z The Z coordinate.
|
||||
* @param T The T coordinate.
|
||||
* @param precomputeDoubleOnly If true, populate dblPrecmp, else set to null.
|
||||
* @return The group element in P3 representation.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public static GroupElement p3(
|
||||
final Curve curve,
|
||||
final FieldElement X,
|
||||
final FieldElement Y,
|
||||
final FieldElement Z,
|
||||
final FieldElement T,
|
||||
final boolean precomputeDoubleOnly) {
|
||||
return new GroupElement(curve, Representation.P3, X, Y, Z, T, precomputeDoubleOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,7 +202,7 @@ public class GroupElement implements Serializable {
|
||||
* <p>
|
||||
* Variable is package private only so that tests run.
|
||||
*/
|
||||
GroupElement[][] precmp;
|
||||
final GroupElement[][] precmp;
|
||||
|
||||
/**
|
||||
* Precomputed table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])},
|
||||
@ -183,10 +210,10 @@ public class GroupElement implements Serializable {
|
||||
* <p>
|
||||
* Variable is package private only so that tests run.
|
||||
*/
|
||||
GroupElement[] dblPrecmp;
|
||||
final GroupElement[] dblPrecmp;
|
||||
|
||||
/**
|
||||
* Creates a group element for a curve.
|
||||
* Creates a group element for a curve, without any pre-computation.
|
||||
*
|
||||
* @param curve The curve.
|
||||
* @param repr The representation used to represent the group element.
|
||||
@ -202,12 +229,37 @@ public class GroupElement implements Serializable {
|
||||
final FieldElement Y,
|
||||
final FieldElement Z,
|
||||
final FieldElement T) {
|
||||
this(curve, repr, X, Y, Z, T, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group element for a curve, with optional pre-computation.
|
||||
*
|
||||
* @param curve The curve.
|
||||
* @param repr The representation used to represent the group element.
|
||||
* @param X The X coordinate.
|
||||
* @param Y The Y coordinate.
|
||||
* @param Z The Z coordinate.
|
||||
* @param T The T coordinate.
|
||||
* @param precomputeDouble If true, populate dblPrecmp, else set to null.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public GroupElement(
|
||||
final Curve curve,
|
||||
final Representation repr,
|
||||
final FieldElement X,
|
||||
final FieldElement Y,
|
||||
final FieldElement Z,
|
||||
final FieldElement T,
|
||||
final boolean precomputeDouble) {
|
||||
this.curve = curve;
|
||||
this.repr = repr;
|
||||
this.X = X;
|
||||
this.Y = Y;
|
||||
this.Z = Z;
|
||||
this.T = T;
|
||||
this.precmp = null;
|
||||
this.dblPrecmp = precomputeDouble ? precomputeDouble() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,8 +267,28 @@ public class GroupElement implements Serializable {
|
||||
* <p>
|
||||
* A point (x,y) is encoded by storing y in bit 0 to bit 254 and the sign of x in bit 255.
|
||||
* x is recovered in the following way:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>x = sign(x) * sqrt((y^2 - 1) / (d * y^2 + 1)) = sign(x) * sqrt(u / v) with u = y^2 - 1 and v = d * y^2 + 1.
|
||||
* <li>Setting = (u * v^3) * (u * v^7)^((q - 5) / 8) one has ^2 = +-(u / v).
|
||||
* <li>If v * = -u multiply with i=sqrt(-1).
|
||||
* <li>Set x := .
|
||||
* <li>If sign(x) != bit 255 of s then negate x.
|
||||
* </ul>
|
||||
*
|
||||
* @param curve The curve.
|
||||
* @param s The encoded point.
|
||||
*/
|
||||
public GroupElement(final Curve curve, final byte[] s) {
|
||||
this(curve, s, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group element for a curve from a given encoded point. With optional pre-computation.
|
||||
* <p>
|
||||
* A point (x,y) is encoded by storing y in bit 0 to bit 254 and the sign of x in bit 255.
|
||||
* x is recovered in the following way:
|
||||
* </p><ul>
|
||||
* <li>x = sign(x) * \sqrt{(y^2 - 1) / (d * y^2 + 1)} = sign(x) * \sqrt{u / v} with u = y^2 - 1 and v = d * y^2 + 1.
|
||||
* <li>Setting β = (u * v^3) * (u * v^7)^((q - 5) / 8) one has β^2 = +-(u / v).
|
||||
* <li>If v * β = -u multiply β with i=sqrt(-1).
|
||||
* <li>Set x := β.
|
||||
@ -225,8 +297,10 @@ public class GroupElement implements Serializable {
|
||||
*
|
||||
* @param curve The curve.
|
||||
* @param s The encoded point.
|
||||
* @param precomputeSingleAndDouble If true, populate both precmp and dblPrecmp, else set both to null.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public GroupElement(final Curve curve, final byte[] s) {
|
||||
public GroupElement(final Curve curve, final byte[] s, boolean precomputeSingleAndDouble) {
|
||||
FieldElement x, y, yy, u, v, v3, vxx, check;
|
||||
y = curve.getField().fromByteArray(s);
|
||||
yy = y.square();
|
||||
@ -241,7 +315,7 @@ public class GroupElement implements Serializable {
|
||||
v3 = v.square().multiply(v);
|
||||
|
||||
// x = (v3^2)vu, aka x = uv^7
|
||||
x = v3.square().multiply(v).multiply(u);
|
||||
x = v3.square().multiply(v).multiply(u);
|
||||
|
||||
// x = (uv^7)^((q-5)/8)
|
||||
x = x.pow22523();
|
||||
@ -269,6 +343,13 @@ public class GroupElement implements Serializable {
|
||||
this.Y = y;
|
||||
this.Z = curve.getField().ONE;
|
||||
this.T = this.X.multiply(this.Y);
|
||||
if (precomputeSingleAndDouble) {
|
||||
precmp = precomputeSingle();
|
||||
dblPrecmp = precomputeDouble();
|
||||
} else {
|
||||
precmp = null;
|
||||
dblPrecmp = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,6 +448,16 @@ public class GroupElement implements Serializable {
|
||||
return toRep(Representation.P3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the group element to the P3 representation, with dblPrecmp populated.
|
||||
*
|
||||
* @return The group element in the P3 representation.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
public GroupElement toP3PrecomputeDouble() {
|
||||
return toRep(Representation.P3PrecomputedDouble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the group element to the CACHED representation.
|
||||
*
|
||||
@ -382,7 +473,7 @@ public class GroupElement implements Serializable {
|
||||
* r = p
|
||||
* <p>
|
||||
* Supported conversions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>P3 -> P2
|
||||
* <li>P3 -> CACHED (1 multiply, 1 add, 1 subtract)
|
||||
* <li>P1P1 -> P2 (3 multiply)
|
||||
@ -416,7 +507,9 @@ public class GroupElement implements Serializable {
|
||||
case P2:
|
||||
return p2(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T));
|
||||
case P3:
|
||||
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y));
|
||||
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y), false);
|
||||
case P3PrecomputedDouble:
|
||||
return p3(this.curve, this.X.multiply(this.T), Y.multiply(this.Z), this.Z.multiply(this.T), this.X.multiply(this.Y), true);
|
||||
case P1P1:
|
||||
return p1p1(this.curve, this.X, this.Y, this.Z, this.T);
|
||||
default:
|
||||
@ -442,51 +535,49 @@ public class GroupElement implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Precomputes several tables.
|
||||
* <p>
|
||||
* The precomputed tables are used for {@link #scalarMultiply(byte[])}
|
||||
* and {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}.
|
||||
*
|
||||
* @param precomputeSingle should the matrix for scalarMultiply() be precomputed?
|
||||
* Precomputes table for {@link #scalarMultiply(byte[])}.
|
||||
* @since 0.9.36 split out from precompute()
|
||||
*/
|
||||
public synchronized void precompute(final boolean precomputeSingle) {
|
||||
GroupElement Bi;
|
||||
|
||||
if (precomputeSingle && this.precmp == null) {
|
||||
// Precomputation for single scalar multiplication.
|
||||
this.precmp = new GroupElement[32][8];
|
||||
// TODO-CR BR: check that this == base point when the method is called.
|
||||
Bi = this;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
GroupElement Bij = Bi;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
final FieldElement recip = Bij.Z.invert();
|
||||
final FieldElement x = Bij.X.multiply(recip);
|
||||
final FieldElement y = Bij.Y.multiply(recip);
|
||||
this.precmp[i][j] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
|
||||
Bij = Bij.add(Bi.toCached()).toP3();
|
||||
}
|
||||
// Only every second summand is precomputed (16^2 = 256)
|
||||
for (int k = 0; k < 8; k++) {
|
||||
Bi = Bi.add(Bi.toCached()).toP3();
|
||||
}
|
||||
private GroupElement[][] precomputeSingle() {
|
||||
// Precomputation for single scalar multiplication.
|
||||
GroupElement[][] precmp = new GroupElement[32][8];
|
||||
// TODO-CR BR: check that this == base point when the method is called.
|
||||
GroupElement Bi = this;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
GroupElement Bij = Bi;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
final FieldElement recip = Bij.Z.invert();
|
||||
final FieldElement x = Bij.X.multiply(recip);
|
||||
final FieldElement y = Bij.Y.multiply(recip);
|
||||
precmp[i][j] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
|
||||
Bij = Bij.add(Bi.toCached()).toP3();
|
||||
}
|
||||
// Only every second summand is precomputed (16^2 = 256)
|
||||
for (int k = 0; k < 8; k++) {
|
||||
Bi = Bi.add(Bi.toCached()).toP3();
|
||||
}
|
||||
}
|
||||
return precmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Precomputes table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}.
|
||||
* @since 0.9.36 split out from precompute()
|
||||
*/
|
||||
private GroupElement[] precomputeDouble() {
|
||||
// Precomputation for double scalar multiplication.
|
||||
// P,3P,5P,7P,9P,11P,13P,15P
|
||||
if (this.dblPrecmp != null)
|
||||
return;
|
||||
this.dblPrecmp = new GroupElement[8];
|
||||
Bi = this;
|
||||
GroupElement[] dblPrecmp = new GroupElement[8];
|
||||
GroupElement Bi = this;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
final FieldElement recip = Bi.Z.invert();
|
||||
final FieldElement x = Bi.X.multiply(recip);
|
||||
final FieldElement y = Bi.Y.multiply(recip);
|
||||
this.dblPrecmp[i] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
|
||||
dblPrecmp[i] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D()));
|
||||
// Bi = edwards(B,edwards(B,Bi))
|
||||
Bi = this.add(this.add(Bi.toCached()).toP3().toCached()).toP3();
|
||||
}
|
||||
return dblPrecmp;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,7 +587,7 @@ public class GroupElement implements Serializable {
|
||||
* r in P x P representation:
|
||||
* <p>
|
||||
* r = ((X' : Z'), (Y' : T')) where
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X' = (X + Y)^2 - (Y^2 + X^2)
|
||||
* <li>Y' = Y^2 + X^2
|
||||
* <li>Z' = y^2 - X^2
|
||||
@ -505,7 +596,7 @@ public class GroupElement implements Serializable {
|
||||
* r converted from P x P to P^2 representation:
|
||||
* <p>
|
||||
* r = (X'' : Y'' : Z'') where
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X'' = X' * Z' = ((X + Y)^2 - Y^2 - X^2) * (2 * Z^2 - (y^2 - X^2))
|
||||
* <li>Y'' = Y' * T' = (Y^2 + X^2) * (2 * Z^2 - (y^2 - X^2))
|
||||
* <li>Z'' = Z' * T' = (y^2 - X^2) * (2 * Z^2 - (y^2 - X^2))
|
||||
@ -548,14 +639,14 @@ public class GroupElement implements Serializable {
|
||||
* r in P x P representation:
|
||||
* <p>
|
||||
* r = ((X' : Z'), (Y' : T')) where
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X' = (Y1 + X1) * q.X - (Y1 - X1) * q.Y = ((Y1 + X1) * (Y2 + X2) - (Y1 - X1) * (Y2 - X2)) * 1/Z2
|
||||
* <li>Y' = (Y1 + X1) * q.X + (Y1 - X1) * q.Y = ((Y1 + X1) * (Y2 + X2) + (Y1 - X1) * (Y2 - X2)) * 1/Z2
|
||||
* <li>Z' = 2 * Z1 + T1 * q.Z = 2 * Z1 + T1 * 2 * d * X2 * Y2 * 1/Z2^2 = (2 * Z1 * Z2 + 2 * d * T1 * T2) * 1/Z2
|
||||
* <li>T' = 2 * Z1 - T1 * q.Z = 2 * Z1 - T1 * 2 * d * X2 * Y2 * 1/Z2^2 = (2 * Z1 * Z2 - 2 * d * T1 * T2) * 1/Z2
|
||||
* </ul><p>
|
||||
* Setting A = (Y1 - X1) * (Y2 - X2), B = (Y1 + X1) * (Y2 + X2), C = 2 * d * T1 * T2, D = 2 * Z1 * Z2 we get
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X' = (B - A) * 1/Z2
|
||||
* <li>Y' = (B + A) * 1/Z2
|
||||
* <li>Z' = (D + C) * 1/Z2
|
||||
@ -564,7 +655,7 @@ public class GroupElement implements Serializable {
|
||||
* r converted from P x P to P^2 representation:
|
||||
* <p>
|
||||
* r = (X'' : Y'' : Z'' : T'') where
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X'' = X' * Z' = (B - A) * (D + C) * 1/Z2^2
|
||||
* <li>Y'' = Y' * T' = (B + A) * (D - C) * 1/Z2^2
|
||||
* <li>Z'' = Z' * T' = (D + C) * (D - C) * 1/Z2^2
|
||||
@ -634,14 +725,14 @@ public class GroupElement implements Serializable {
|
||||
* r = p + q where p = this = (X1 : Y1 : Z1 : T1), q = (q.X, q.Y, q.Z, q.T) = (Y2 + X2, Y2 - X2, Z2, 2 * d * T2)
|
||||
* <p>
|
||||
* r in P x P representation:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X' = (Y1 + X1) * (Y2 + X2) - (Y1 - X1) * (Y2 - X2)
|
||||
* <li>Y' = (Y1 + X1) * (Y2 + X2) + (Y1 - X1) * (Y2 - X2)
|
||||
* <li>Z' = 2 * Z1 * Z2 + 2 * d * T1 * T2
|
||||
* <li>T' = 2 * Z1 * T2 - 2 * d * T1 * T2
|
||||
* </ul><p>
|
||||
* Setting A = (Y1 - X1) * (Y2 - X2), B = (Y1 + X1) * (Y2 + X2), C = 2 * d * T1 * T2, D = 2 * Z1 * Z2 we get
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>X' = (B - A)
|
||||
* <li>Y' = (B + A)
|
||||
* <li>Z' = (D + C)
|
||||
@ -708,7 +799,7 @@ public class GroupElement implements Serializable {
|
||||
public GroupElement negate() {
|
||||
if (this.repr != Representation.P3)
|
||||
throw new UnsupportedOperationException();
|
||||
return this.curve.getZero(Representation.P3).sub(toCached()).toP3();
|
||||
return this.curve.getZero(Representation.P3).sub(toCached()).toP3PrecomputeDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -804,19 +895,10 @@ public class GroupElement implements Serializable {
|
||||
*
|
||||
* @param u The group element to return if b == 1.
|
||||
* @param b in {0, 1}
|
||||
* @return u if b == 1; this if b == 0; null otherwise.
|
||||
* @return u if b == 1; this if b == 0; Results undefined if b is not in {0, 1}.
|
||||
*/
|
||||
GroupElement cmov(final GroupElement u, final int b) {
|
||||
GroupElement ret = null;
|
||||
for (int i = 0; i < b; i++) {
|
||||
// Only for b == 1
|
||||
ret = u;
|
||||
}
|
||||
for (int i = 0; i < 1-b; i++) {
|
||||
// Only for b == 0
|
||||
ret = this;
|
||||
}
|
||||
return ret;
|
||||
return precomp(curve, X.cmov(u.X, b), Y.cmov(u.Y, b), Z.cmov(u.Z, b));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -873,22 +955,16 @@ public class GroupElement implements Serializable {
|
||||
final byte[] e = toRadix16(a);
|
||||
|
||||
GroupElement h = this.curve.getZero(Representation.P3);
|
||||
synchronized(this) {
|
||||
// TODO: Get opinion from a crypto professional.
|
||||
// This should in practice never be necessary, the only point that
|
||||
// this should get called on is EdDSA's B.
|
||||
//precompute();
|
||||
for (i = 1; i < 64; i += 2) {
|
||||
t = select(i/2, e[i]);
|
||||
h = h.madd(t).toP3();
|
||||
}
|
||||
for (i = 1; i < 64; i += 2) {
|
||||
t = select(i/2, e[i]);
|
||||
h = h.madd(t).toP3();
|
||||
}
|
||||
|
||||
h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
|
||||
h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
|
||||
|
||||
for (i = 0; i < 64; i += 2) {
|
||||
t = select(i/2, e[i]);
|
||||
h = h.madd(t).toP3();
|
||||
}
|
||||
for (i = 0; i < 64; i += 2) {
|
||||
t = select(i/2, e[i]);
|
||||
h = h.madd(t).toP3();
|
||||
}
|
||||
|
||||
return h;
|
||||
@ -965,14 +1041,8 @@ public class GroupElement implements Serializable {
|
||||
if (aslide[i] != 0 || bslide[i] != 0) break;
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
// TODO-CR BR strange comment below.
|
||||
// TODO: Get opinion from a crypto professional.
|
||||
// This should in practice never be necessary, the only point that
|
||||
// this should get called on is EdDSA's B.
|
||||
//precompute();
|
||||
for (; i >= 0; --i) {
|
||||
GroupElement t = r.dbl();
|
||||
for (; i >= 0; --i) {
|
||||
GroupElement t = r.dbl();
|
||||
|
||||
if (aslide[i] > 0) {
|
||||
t = t.toP3().madd(A.dblPrecmp[aslide[i]/2]);
|
||||
@ -986,8 +1056,7 @@ public class GroupElement implements Serializable {
|
||||
t = t.toP3().msub(this.dblPrecmp[(-bslide[i])/2]);
|
||||
}
|
||||
|
||||
r = t.toP2();
|
||||
}
|
||||
r = t.toP2();
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -93,6 +93,16 @@ public class BigIntegerFieldElement extends FieldElement implements Serializable
|
||||
return pow(f.getQm5d8());
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.36
|
||||
*/
|
||||
@Override
|
||||
public FieldElement cmov(FieldElement val, int b) {
|
||||
// Not constant-time, but it doesn't really matter because none of the underlying BigInteger operations
|
||||
// are either, so there's not much point in trying hard here ...
|
||||
return b == 0 ? this : val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return bi.hashCode();
|
||||
|
@ -51,12 +51,12 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* TODO-CR BR: h is allocated via new, probably not a good idea. Do we need the copying into temp variables if we do that?
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* <li>|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
* </ul>
|
||||
*
|
||||
@ -80,12 +80,12 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* TODO-CR BR: See above.
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* <li>|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
|
||||
* </ul>
|
||||
*
|
||||
@ -107,11 +107,11 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* TODO-CR BR: see above.
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
|
||||
* </ul>
|
||||
*
|
||||
@ -131,14 +131,14 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* Can overlap h with f or g.
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by
|
||||
* 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
* <li>|g| bounded by
|
||||
* 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by
|
||||
* 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
* </ul><p>
|
||||
@ -382,11 +382,11 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* Can overlap h with f.
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
* </ul><p>
|
||||
* See {@link #multiply(FieldElement)} for discussion
|
||||
@ -538,11 +538,11 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
* Can overlap h with f.
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
|
||||
* </ul><p>
|
||||
* Postconditions:
|
||||
* <p><ul>
|
||||
* </p><ul>
|
||||
* <li>|h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
|
||||
* </ul><p>
|
||||
* See {@link #multiply(FieldElement)} for discussion
|
||||
@ -934,6 +934,30 @@ public class Ed25519FieldElement extends FieldElement {
|
||||
return multiply(t0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant-time conditional move. Well, actually it is a conditional copy.
|
||||
* Logic is inspired by the SUPERCOP implementation at:
|
||||
* https://github.com/floodyberry/supercop/blob/master/crypto_sign/ed25519/ref10/fe_cmov.c
|
||||
*
|
||||
* @param val the other field element.
|
||||
* @param b must be 0 or 1, otherwise results are undefined.
|
||||
* @return a copy of this if b == 0, or a copy of val if b == 1.
|
||||
* @since 0.9.36
|
||||
*/
|
||||
@Override
|
||||
public FieldElement cmov(FieldElement val, int b) {
|
||||
Ed25519FieldElement that = (Ed25519FieldElement) val;
|
||||
b = -b;
|
||||
int[] result = new int[10];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
result[i] = this.t[i];
|
||||
int x = this.t[i] ^ that.t[i];
|
||||
x &= b;
|
||||
result[i] ^= x;
|
||||
}
|
||||
return new Ed25519FieldElement(this.f, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(t);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p.crypto.eddsa.spec;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.i2p.crypto.eddsa.Utils;
|
||||
import net.i2p.crypto.eddsa.math.Curve;
|
||||
@ -16,6 +17,9 @@ import net.i2p.crypto.eddsa.math.ed25519.Ed25519ScalarOps;
|
||||
*
|
||||
*/
|
||||
public class EdDSANamedCurveTable {
|
||||
/** RFC 8032 */
|
||||
public static final String ED_25519 = "Ed25519";
|
||||
/** old name */
|
||||
public static final String CURVE_ED25519_SHA512 = "ed25519-sha-512";
|
||||
|
||||
private static final Field ed25519field = new Field(
|
||||
@ -27,8 +31,8 @@ public class EdDSANamedCurveTable {
|
||||
Utils.hexToBytes("a3785913ca4deb75abd841414d0a700098e879777940c78c73fe6f2bee6c0352"), // d
|
||||
ed25519field.fromByteArray(Utils.hexToBytes("b0a00e4a271beec478e42fad0618432fa7d7fb3d99004d2b0bdfc14f8024832b"))); // I
|
||||
|
||||
private static final EdDSANamedCurveSpec ed25519sha512 = new EdDSANamedCurveSpec(
|
||||
CURVE_ED25519_SHA512,
|
||||
public static final EdDSANamedCurveSpec ED_25519_CURVE_SPEC = new EdDSANamedCurveSpec(
|
||||
ED_25519,
|
||||
ed25519curve,
|
||||
"SHA-512", // H
|
||||
new Ed25519ScalarOps(), // l
|
||||
@ -36,17 +40,34 @@ public class EdDSANamedCurveTable {
|
||||
Utils.hexToBytes("5866666666666666666666666666666666666666666666666666666666666666"),
|
||||
true)); // Precompute tables for B
|
||||
|
||||
private static final Hashtable<String, EdDSANamedCurveSpec> curves = new Hashtable<String, EdDSANamedCurveSpec>();
|
||||
private static volatile HashMap<String, EdDSANamedCurveSpec> curves = new HashMap<String, EdDSANamedCurveSpec>();
|
||||
|
||||
public static void defineCurve(String name, EdDSANamedCurveSpec curve) {
|
||||
curves.put(name, curve);
|
||||
private static synchronized void putCurve(String name, EdDSANamedCurveSpec curve) {
|
||||
HashMap<String, EdDSANamedCurveSpec> newCurves = new HashMap<String, EdDSANamedCurveSpec>(curves);
|
||||
newCurves.put(name, curve);
|
||||
curves = newCurves;
|
||||
}
|
||||
|
||||
public static void defineCurve(EdDSANamedCurveSpec curve) {
|
||||
putCurve(curve.getName().toLowerCase(Locale.ENGLISH), curve);
|
||||
}
|
||||
|
||||
static void defineCurveAlias(String name, String alias) {
|
||||
EdDSANamedCurveSpec curve = curves.get(name.toLowerCase(Locale.ENGLISH));
|
||||
if (curve == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
putCurve(alias.toLowerCase(Locale.ENGLISH), curve);
|
||||
}
|
||||
|
||||
static {
|
||||
defineCurve(CURVE_ED25519_SHA512, ed25519sha512);
|
||||
// RFC 8032
|
||||
defineCurve(ED_25519_CURVE_SPEC);
|
||||
// old name
|
||||
defineCurveAlias(ED_25519, CURVE_ED25519_SHA512);
|
||||
}
|
||||
|
||||
public static EdDSANamedCurveSpec getByName(String name) {
|
||||
return curves.get(name);
|
||||
return curves.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,10 @@ public class EdDSAParameterSpec implements AlgorithmParameterSpec, Serializable
|
||||
private final GroupElement B;
|
||||
|
||||
/**
|
||||
* @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,
|
||||
|
@ -21,6 +21,8 @@ public class EdDSAPrivateKeySpec implements KeySpec {
|
||||
private final EdDSAParameterSpec spec;
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
@ -58,6 +60,7 @@ public class EdDSAPrivateKeySpec implements KeySpec {
|
||||
* 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)
|
||||
|
@ -12,10 +12,12 @@ import net.i2p.crypto.eddsa.math.GroupElement;
|
||||
*/
|
||||
public class EdDSAPublicKeySpec implements KeySpec {
|
||||
private final GroupElement A;
|
||||
private final GroupElement Aneg;
|
||||
private GroupElement Aneg;
|
||||
private final EdDSAParameterSpec spec;
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
@ -23,16 +25,11 @@ public class EdDSAPublicKeySpec implements KeySpec {
|
||||
throw new IllegalArgumentException("public-key length is wrong");
|
||||
|
||||
this.A = new GroupElement(spec.getCurve(), pk);
|
||||
// Precompute -A for use in verification.
|
||||
this.Aneg = A.negate();
|
||||
Aneg.precompute(false);
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public EdDSAPublicKeySpec(GroupElement A, EdDSAParameterSpec spec) {
|
||||
this.A = A;
|
||||
this.Aneg = A.negate();
|
||||
Aneg.precompute(false);
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
@ -41,7 +38,13 @@ public class EdDSAPublicKeySpec implements KeySpec {
|
||||
}
|
||||
|
||||
public GroupElement getNegativeA() {
|
||||
return Aneg;
|
||||
// Only read Aneg once, otherwise read re-ordering might occur between here and return. Requires all GroupElement's fields to be final.
|
||||
GroupElement ourAneg = Aneg;
|
||||
if(ourAneg == null) {
|
||||
ourAneg = A.negate();
|
||||
Aneg = ourAneg;
|
||||
}
|
||||
return ourAneg;
|
||||
}
|
||||
|
||||
public EdDSAParameterSpec getParams() {
|
||||
|
@ -1,6 +1,9 @@
|
||||
<html><body>
|
||||
<p>
|
||||
Specifications for curves and keys, and a table for named curves,
|
||||
initially containing only the 25519 curve "ed25519-sha-512".
|
||||
Specifications for curves and keys, and a table for named curves.
|
||||
Contains the following curves:
|
||||
</p>
|
||||
<ul>
|
||||
<li>"Ed25519"</li>
|
||||
</ul>
|
||||
</body></html>
|
||||
|
@ -760,7 +760,7 @@ public class GroupElementTest {
|
||||
GroupElement A = new GroupElement(curve, Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66"));
|
||||
GroupElement B = ed25519.getB();
|
||||
GroupElement geZero = curve.getZero(GroupElement.Representation.P3);
|
||||
geZero.precompute(false);
|
||||
//geZero.precompute(false);
|
||||
|
||||
// 0 * GE(0) + 0 * GE(0) = GE(0)
|
||||
assertThat(geZero.doubleScalarMultiplyVariableTime(geZero, zero, zero),
|
||||
@ -802,7 +802,7 @@ public class GroupElementTest {
|
||||
// Arrange:
|
||||
final GroupElement basePoint = ed25519.getB();
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
g.precompute(false);
|
||||
//g.precompute(false);
|
||||
final FieldElement f1 = MathUtils.getRandomFieldElement();
|
||||
final FieldElement f2 = MathUtils.getRandomFieldElement();
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
2018-07-01 zzz
|
||||
* Crypto: Backport EdDSA versions 0.2/0.3 from github
|
||||
|
||||
2018-06-30 zzz
|
||||
* Console: Fix reading flags when symlinked (ticket #2270)
|
||||
* Router: Reselect jbigi lib when processor changes (ticket #2277)
|
||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 2;
|
||||
public final static long BUILD = 3;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
Reference in New Issue
Block a user