mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 15:19:53 +01:00
Merge branch '236-curve25519' into 'master'
Use Curve25519 for key agreement Closes #236 See merge request akwizgran/briar!693
This commit is contained in:
@@ -5,7 +5,7 @@ public interface CryptoConstants {
|
|||||||
/**
|
/**
|
||||||
* The maximum length of an agreement public key in bytes.
|
* The maximum length of an agreement public key in bytes.
|
||||||
*/
|
*/
|
||||||
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 65;
|
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a signature public key in bytes.
|
* The maximum length of a signature public key in bytes.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dependencies {
|
|||||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
|
implementation 'org.whispersystems:curve25519-java:0.4.1'
|
||||||
|
|
||||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ dependencyVerification {
|
|||||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
|
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,15 +14,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
|
||||||
import org.spongycastle.crypto.CryptoException;
|
import org.spongycastle.crypto.CryptoException;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
|
||||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|
||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -35,7 +31,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -44,7 +39,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(CryptoComponentImpl.class.getName());
|
Logger.getLogger(CryptoComponentImpl.class.getName());
|
||||||
|
|
||||||
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||||
@@ -52,7 +46,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final PasswordBasedKdf passwordBasedKdf;
|
private final PasswordBasedKdf passwordBasedKdf;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final Curve25519 curve25519;
|
||||||
private final KeyPairGenerator signatureKeyPairGenerator;
|
private final KeyPairGenerator signatureKeyPairGenerator;
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
@@ -80,15 +74,11 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
secureRandom = new SecureRandom();
|
secureRandom = new SecureRandom();
|
||||||
this.passwordBasedKdf = passwordBasedKdf;
|
this.passwordBasedKdf = passwordBasedKdf;
|
||||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
curve25519 = Curve25519.getInstance("java");
|
||||||
PARAMETERS, secureRandom);
|
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
|
||||||
agreementKeyPairGenerator.init(params);
|
|
||||||
signatureKeyPairGenerator = new KeyPairGenerator();
|
signatureKeyPairGenerator = new KeyPairGenerator();
|
||||||
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
||||||
secureRandom);
|
secureRandom);
|
||||||
agreementKeyParser = new Sec1KeyParser(PARAMETERS,
|
agreementKeyParser = new Curve25519KeyParser();
|
||||||
AGREEMENT_KEY_PAIR_BITS);
|
|
||||||
signatureKeyParser = new EdKeyParser();
|
signatureKeyParser = new EdKeyParser();
|
||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
}
|
}
|
||||||
@@ -133,16 +123,17 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// Package access for testing
|
// Package access for testing
|
||||||
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (!(priv instanceof Sec1PrivateKey))
|
if (!(priv instanceof Curve25519PrivateKey))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (!(pub instanceof Sec1PublicKey))
|
if (!(pub instanceof Curve25519PublicKey))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
ECPrivateKeyParameters ecPriv = ((Sec1PrivateKey) priv).getKey();
|
|
||||||
ECPublicKeyParameters ecPub = ((Sec1PublicKey) pub).getKey();
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
|
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
|
||||||
agreement.init(ecPriv);
|
priv.getEncoded());
|
||||||
byte[] secret = agreement.calculateAgreement(ecPub).toByteArray();
|
// If the shared secret is all zeroes, the public key is invalid
|
||||||
|
byte allZero = 0;
|
||||||
|
for (byte b : secret) allZero |= b;
|
||||||
|
if (allZero == 0) throw new GeneralSecurityException();
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Deriving shared secret took " + duration + " ms");
|
LOG.info("Deriving shared secret took " + duration + " ms");
|
||||||
@@ -151,18 +142,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
AsymmetricCipherKeyPair keyPair =
|
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
|
||||||
agreementKeyPairGenerator.generateKeyPair();
|
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
|
||||||
// Return a wrapper that uses the SEC 1 encoding
|
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
|
||||||
ECPublicKeyParameters ecPublicKey =
|
return new KeyPair(pub, priv);
|
||||||
(ECPublicKeyParameters) keyPair.getPublic();
|
|
||||||
PublicKey publicKey = new Sec1PublicKey(ecPublicKey
|
|
||||||
);
|
|
||||||
ECPrivateKeyParameters ecPrivateKey =
|
|
||||||
(ECPrivateKeyParameters) keyPair.getPrivate();
|
|
||||||
PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey,
|
|
||||||
AGREEMENT_KEY_PAIR_BITS);
|
|
||||||
return new KeyPair(publicKey, privateKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -172,7 +155,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateSignatureKeyPair() {
|
public KeyPair generateSignatureKeyPair() {
|
||||||
java.security.KeyPair keyPair = signatureKeyPairGenerator.generateKeyPair();
|
java.security.KeyPair keyPair =
|
||||||
|
signatureKeyPairGenerator.generateKeyPair();
|
||||||
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||||
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||||
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class Curve25519KeyParser implements KeyParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new Curve25519PublicKey(encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new Curve25519PrivateKey(clamp(encodedKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] clamp(byte[] b) {
|
||||||
|
byte[] clamped = new byte[32];
|
||||||
|
System.arraycopy(b, 0, clamped, 0, 32);
|
||||||
|
clamped[0] &= 248;
|
||||||
|
clamped[31] &= 127;
|
||||||
|
clamped[31] |= 64;
|
||||||
|
return clamped;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class Curve25519PrivateKey extends Bytes implements PrivateKey {
|
||||||
|
|
||||||
|
Curve25519PrivateKey(byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class Curve25519PublicKey extends Bytes implements PublicKey {
|
||||||
|
|
||||||
|
Curve25519PublicKey(byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
|
||||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
|
||||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
|
||||||
import org.spongycastle.math.ec.ECCurve;
|
|
||||||
import org.spongycastle.math.ec.ECMultiplier;
|
|
||||||
import org.spongycastle.math.ec.ECPoint;
|
|
||||||
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameters for curve brainpoolp256r1 - see RFC 5639.
|
|
||||||
*/
|
|
||||||
class EllipticCurveConstants {
|
|
||||||
|
|
||||||
static final ECDomainParameters PARAMETERS;
|
|
||||||
|
|
||||||
static {
|
|
||||||
// Start with the default implementation of the curve
|
|
||||||
X9ECParameters x9 = TeleTrusTNamedCurves.getByName("brainpoolp256r1");
|
|
||||||
// Use a constant-time multiplier
|
|
||||||
ECMultiplier monty = new MontgomeryLadderMultiplier();
|
|
||||||
ECCurve curve = x9.getCurve().configure().setMultiplier(monty).create();
|
|
||||||
BigInteger gX = x9.getG().getAffineXCoord().toBigInteger();
|
|
||||||
BigInteger gY = x9.getG().getAffineYCoord().toBigInteger();
|
|
||||||
ECPoint g = curve.createPoint(gX, gY);
|
|
||||||
// Convert to ECDomainParameters using the new multiplier
|
|
||||||
PARAMETERS = new ECDomainParameters(curve, g, x9.getN(), x9.getH());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,11 +13,11 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.math.ec.ECCurve;
|
import org.spongycastle.math.ec.ECCurve;
|
||||||
import org.spongycastle.math.ec.ECPoint;
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
|
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
||||||
@@ -31,15 +31,11 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
ECPoint defaultG = defaultX9Parameters.getG();
|
ECPoint defaultG = defaultX9Parameters.getG();
|
||||||
BigInteger defaultN = defaultX9Parameters.getN();
|
BigInteger defaultN = defaultX9Parameters.getN();
|
||||||
BigInteger defaultH = defaultX9Parameters.getH();
|
BigInteger defaultH = defaultX9Parameters.getH();
|
||||||
// Check that the default parameters are equal to our parameters
|
|
||||||
assertEquals(PARAMETERS.getCurve(), defaultCurve);
|
|
||||||
assertEquals(PARAMETERS.getG(), defaultG);
|
|
||||||
assertEquals(PARAMETERS.getN(), defaultN);
|
|
||||||
assertEquals(PARAMETERS.getH(), defaultH);
|
|
||||||
// ECDomainParameters doesn't have an equals() method, but it's just a
|
|
||||||
// container for the parameters
|
|
||||||
ECDomainParameters defaultParameters = new ECDomainParameters(
|
ECDomainParameters defaultParameters = new ECDomainParameters(
|
||||||
defaultCurve, defaultG, defaultN, defaultH);
|
defaultCurve, defaultG, defaultN, defaultH);
|
||||||
|
// Instantiate an implementation using the Montgomery ladder multiplier
|
||||||
|
ECDomainParameters montgomeryParameters =
|
||||||
|
constantTime(defaultParameters);
|
||||||
// Generate two key pairs with each set of parameters, using the same
|
// Generate two key pairs with each set of parameters, using the same
|
||||||
// deterministic PRNG for both sets of parameters
|
// deterministic PRNG for both sets of parameters
|
||||||
byte[] seed = new byte[32];
|
byte[] seed = new byte[32];
|
||||||
@@ -47,7 +43,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
// Montgomery ladder multiplier
|
// Montgomery ladder multiplier
|
||||||
SecureRandom random = new PseudoSecureRandom(seed);
|
SecureRandom random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters montgomeryGeneratorParams =
|
ECKeyGenerationParameters montgomeryGeneratorParams =
|
||||||
new ECKeyGenerationParameters(PARAMETERS, random);
|
new ECKeyGenerationParameters(montgomeryParameters, random);
|
||||||
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
||||||
montgomeryGenerator.init(montgomeryGeneratorParams);
|
montgomeryGenerator.init(montgomeryGeneratorParams);
|
||||||
AsymmetricCipherKeyPair montgomeryKeyPair1 =
|
AsymmetricCipherKeyPair montgomeryKeyPair1 =
|
||||||
@@ -107,4 +103,13 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
assertEquals(sharedSecretMontgomeryMontgomery,
|
assertEquals(sharedSecretMontgomeryMontgomery,
|
||||||
sharedSecretDefaultDefault);
|
sharedSecretDefaultDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ECDomainParameters constantTime(ECDomainParameters in) {
|
||||||
|
ECCurve curve = in.getCurve().configure().setMultiplier(
|
||||||
|
new MontgomeryLadderMultiplier()).create();
|
||||||
|
BigInteger x = in.getG().getAffineXCoord().toBigInteger();
|
||||||
|
BigInteger y = in.getG().getAffineYCoord().toBigInteger();
|
||||||
|
ECPoint g = curve.createPoint(x, y);
|
||||||
|
return new ECDomainParameters(curve, g, in.getN(), in.getH());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||||
|
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||||
|
|
||||||
import org.spongycastle.asn1.sec.SECNamedCurves;
|
import org.spongycastle.asn1.sec.SECNamedCurves;
|
||||||
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
|
import org.spongycastle.crypto.BasicAgreement;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
|
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
|
||||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
import org.spongycastle.crypto.params.ECDomainParameters;
|
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|
||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
|
||||||
import org.spongycastle.crypto.params.ParametersWithRandom;
|
import org.spongycastle.crypto.params.ParametersWithRandom;
|
||||||
import org.spongycastle.crypto.signers.DSADigestSigner;
|
import org.spongycastle.crypto.signers.DSADigestSigner;
|
||||||
import org.spongycastle.crypto.signers.DSAKCalculator;
|
import org.spongycastle.crypto.signers.DSAKCalculator;
|
||||||
@@ -20,14 +23,22 @@ import org.spongycastle.crypto.signers.HMacDSAKCalculator;
|
|||||||
import org.spongycastle.math.ec.ECCurve;
|
import org.spongycastle.math.ec.ECCurve;
|
||||||
import org.spongycastle.math.ec.ECPoint;
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||||
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Signature;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||||
|
|
||||||
// Not a JUnit test
|
// Not a JUnit test
|
||||||
public class EllipticCurvePerformanceTest {
|
public class EllipticCurvePerformanceTest {
|
||||||
|
|
||||||
@@ -38,8 +49,9 @@ public class EllipticCurvePerformanceTest {
|
|||||||
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
|
||||||
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
|
||||||
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
|
||||||
|
private static final Provider ED_PROVIDER = new EdDSASecurityProvider();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws GeneralSecurityException {
|
||||||
for (String name : SEC_NAMES) {
|
for (String name : SEC_NAMES) {
|
||||||
ECDomainParameters params =
|
ECDomainParameters params =
|
||||||
convertParams(SECNamedCurves.getByName(name));
|
convertParams(SECNamedCurves.getByName(name));
|
||||||
@@ -52,43 +64,31 @@ public class EllipticCurvePerformanceTest {
|
|||||||
runTest(name + " default", params);
|
runTest(name + " default", params);
|
||||||
runTest(name + " constant", constantTime(params));
|
runTest(name + " constant", constantTime(params));
|
||||||
}
|
}
|
||||||
runTest("ours", EllipticCurveConstants.PARAMETERS);
|
runCurve25519Test();
|
||||||
|
runEd25519Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void runTest(String name, ECDomainParameters params) {
|
private static void runTest(String name, ECDomainParameters params) {
|
||||||
// Generate two key pairs using the given parameters
|
// Generate two key pairs using the given parameters
|
||||||
ECKeyGenerationParameters generatorParams =
|
|
||||||
new ECKeyGenerationParameters(params, random);
|
|
||||||
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
ECKeyPairGenerator generator = new ECKeyPairGenerator();
|
||||||
generator.init(generatorParams);
|
generator.init(new ECKeyGenerationParameters(params, random));
|
||||||
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
|
||||||
ECPublicKeyParameters public1 =
|
|
||||||
(ECPublicKeyParameters) keyPair1.getPublic();
|
|
||||||
ECPrivateKeyParameters private1 =
|
|
||||||
(ECPrivateKeyParameters) keyPair1.getPrivate();
|
|
||||||
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
|
||||||
ECPublicKeyParameters public2 =
|
// Time some ECDH and ECDHC key agreements
|
||||||
(ECPublicKeyParameters) keyPair2.getPublic();
|
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false);
|
||||||
// Time some ECDH key agreements
|
long agreementWithCofactorMedian =
|
||||||
List<Long> samples = new ArrayList<>();
|
runAgreementTest(keyPair1, keyPair2, true);
|
||||||
for (int i = 0; i < SAMPLES; i++) {
|
|
||||||
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
|
|
||||||
long start = System.nanoTime();
|
|
||||||
agreement.init(private1);
|
|
||||||
agreement.calculateAgreement(public2);
|
|
||||||
samples.add(System.nanoTime() - start);
|
|
||||||
}
|
|
||||||
long agreementMedian = median(samples);
|
|
||||||
// Time some signatures
|
// Time some signatures
|
||||||
|
List<Long> samples = new ArrayList<>();
|
||||||
List<byte[]> signatures = new ArrayList<>();
|
List<byte[]> signatures = new ArrayList<>();
|
||||||
samples.clear();
|
|
||||||
for (int i = 0; i < SAMPLES; i++) {
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
Digest digest = new Blake2bDigest(256);
|
Digest digest = new Blake2bDigest(256);
|
||||||
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
||||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||||
calculator), digest);
|
calculator), digest);
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
signer.init(true, new ParametersWithRandom(private1, random));
|
signer.init(true,
|
||||||
|
new ParametersWithRandom(keyPair1.getPrivate(), random));
|
||||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
signatures.add(signer.generateSignature());
|
signatures.add(signer.generateSignature());
|
||||||
samples.add(System.nanoTime() - start);
|
samples.add(System.nanoTime() - start);
|
||||||
@@ -102,17 +102,83 @@ public class EllipticCurvePerformanceTest {
|
|||||||
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
|
||||||
calculator), digest);
|
calculator), digest);
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
signer.init(false, public1);
|
signer.init(false, keyPair1.getPublic());
|
||||||
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
if (!signer.verifySignature(signatures.get(i)))
|
if (!signer.verifySignature(signatures.get(i)))
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
samples.add(System.nanoTime() - start);
|
samples.add(System.nanoTime() - start);
|
||||||
}
|
}
|
||||||
long verificationMedian = median(samples);
|
long verificationMedian = median(samples);
|
||||||
System.out.println(name + ": "
|
System.out.println(String.format("%s: %,d %,d %,d %,d", name,
|
||||||
+ agreementMedian + " "
|
agreementMedian, agreementWithCofactorMedian,
|
||||||
+ signatureMedian + " "
|
signatureMedian, verificationMedian));
|
||||||
+ verificationMedian);
|
}
|
||||||
|
|
||||||
|
private static long runAgreementTest(AsymmetricCipherKeyPair keyPair1,
|
||||||
|
AsymmetricCipherKeyPair keyPair2, boolean withCofactor) {
|
||||||
|
List<Long> samples = new ArrayList<>();
|
||||||
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
|
BasicAgreement agreement = createAgreement(withCofactor);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
agreement.init(keyPair1.getPrivate());
|
||||||
|
agreement.calculateAgreement(keyPair2.getPublic());
|
||||||
|
samples.add(System.nanoTime() - start);
|
||||||
|
}
|
||||||
|
return median(samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BasicAgreement createAgreement(boolean withCofactor) {
|
||||||
|
if (withCofactor) return new ECDHCBasicAgreement();
|
||||||
|
else return new ECDHBasicAgreement();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runCurve25519Test() {
|
||||||
|
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||||
|
Curve25519KeyPair keyPair1 = curve25519.generateKeyPair();
|
||||||
|
Curve25519KeyPair keyPair2 = curve25519.generateKeyPair();
|
||||||
|
// Time some key agreements
|
||||||
|
List<Long> samples = new ArrayList<>();
|
||||||
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
|
long start = System.nanoTime();
|
||||||
|
curve25519.calculateAgreement(keyPair1.getPublicKey(),
|
||||||
|
keyPair2.getPrivateKey());
|
||||||
|
samples.add(System.nanoTime() - start);
|
||||||
|
}
|
||||||
|
long agreementMedian = median(samples);
|
||||||
|
System.out.println(String.format("Curve25519: %,d - - -",
|
||||||
|
agreementMedian));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runEd25519Test() throws GeneralSecurityException {
|
||||||
|
KeyPair keyPair = new KeyPairGenerator().generateKeyPair();
|
||||||
|
// Time some signatures
|
||||||
|
List<Long> samples = new ArrayList<>();
|
||||||
|
List<byte[]> signatures = new ArrayList<>();
|
||||||
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
|
Signature signature =
|
||||||
|
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
signature.initSign(keyPair.getPrivate(), random);
|
||||||
|
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
|
signatures.add(signature.sign());
|
||||||
|
samples.add(System.nanoTime() - start);
|
||||||
|
}
|
||||||
|
long signatureMedian = median(samples);
|
||||||
|
// Time some signature verifications
|
||||||
|
samples.clear();
|
||||||
|
for (int i = 0; i < SAMPLES; i++) {
|
||||||
|
Signature signature =
|
||||||
|
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
signature.initVerify(keyPair.getPublic());
|
||||||
|
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
|
||||||
|
if (!signature.verify(signatures.get(i)))
|
||||||
|
throw new AssertionError();
|
||||||
|
samples.add(System.nanoTime() - start);
|
||||||
|
}
|
||||||
|
long verificationMedian = median(samples);
|
||||||
|
System.out.println(String.format("Ed25519: - - %,d %,d",
|
||||||
|
signatureMedian, verificationMedian));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long median(List<Long> list) {
|
private static long median(List<Long> list) {
|
||||||
|
|||||||
@@ -2,33 +2,80 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
public class KeyAgreementTest extends BrambleTestCase {
|
public class KeyAgreementTest extends BrambleTestCase {
|
||||||
|
|
||||||
@Test
|
// Test vector from RFC 7748: Alice's private and public keys, Bob's
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
// private and public keys, and the shared secret
|
||||||
CryptoComponent crypto =
|
// https://tools.ietf.org/html/rfc7748#section-6.1
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
private static final String ALICE_PRIVATE =
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
private static final String ALICE_PUBLIC =
|
||||||
|
"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
|
||||||
|
private static final String BOB_PRIVATE =
|
||||||
|
"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
|
||||||
|
private static final String BOB_PUBLIC =
|
||||||
|
"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
|
||||||
|
private static final String SHARED_SECRET =
|
||||||
|
"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742";
|
||||||
|
|
||||||
|
private final CryptoComponent crypto =
|
||||||
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
private final byte[][] inputs;
|
||||||
|
|
||||||
|
public KeyAgreementTest() {
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
byte[][] inputs = new byte[random.nextInt(10) + 1][];
|
inputs = new byte[random.nextInt(10) + 1][];
|
||||||
for (int i = 0; i < inputs.length; i++)
|
for (int i = 0; i < inputs.length; i++)
|
||||||
inputs[i] = getRandomBytes(random.nextInt(256));
|
inputs[i] = getRandomBytes(random.nextInt(256));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDerivesSharedSecret() throws Exception {
|
||||||
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
SecretKey aShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
bPair.getPublic(), aPair, inputs);
|
bPair.getPublic(), aPair, inputs);
|
||||||
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
SecretKey bShared = crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
aPair.getPublic(), bPair, inputs);
|
aPair.getPublic(), bPair, inputs);
|
||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = GeneralSecurityException.class)
|
||||||
|
public void testRejectsInvalidPublicKey() throws Exception {
|
||||||
|
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
||||||
|
PublicKey invalid = new Curve25519PublicKey(new byte[32]);
|
||||||
|
crypto.deriveSharedSecret(SHARED_SECRET_LABEL, invalid, keyPair,
|
||||||
|
inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRfc7748TestVector() throws Exception {
|
||||||
|
// Private keys need to be clamped because curve25519-java does the
|
||||||
|
// clamping at key generation time, not multiplication time
|
||||||
|
byte[] aPriv = Curve25519KeyParser.clamp(fromHexString(ALICE_PRIVATE));
|
||||||
|
byte[] aPub = fromHexString(ALICE_PUBLIC);
|
||||||
|
byte[] bPriv = Curve25519KeyParser.clamp(fromHexString(BOB_PRIVATE));
|
||||||
|
byte[] bPub = fromHexString(BOB_PUBLIC);
|
||||||
|
byte[] sharedSecret = fromHexString(SHARED_SECRET);
|
||||||
|
Curve25519 curve25519 = Curve25519.getInstance("java");
|
||||||
|
assertArrayEquals(sharedSecret,
|
||||||
|
curve25519.calculateAgreement(aPub, bPriv));
|
||||||
|
assertArrayEquals(sharedSecret,
|
||||||
|
curve25519.calculateAgreement(bPub, aPriv));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
|||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() throws Exception {
|
||||||
// Generate 10 agreement key pairs
|
// Generate 10 agreement key pairs
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
KeyPair keyPair = crypto.generateAgreementKeyPair();
|
||||||
// Check the length of the public key
|
// Check the length of the public key
|
||||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||||
assertTrue(publicKey.length <= MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
assertTrue(publicKey.length <= MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
||||||
|
|||||||
Reference in New Issue
Block a user