mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Add support for Ed25519 signatures.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
@@ -56,6 +60,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||
private static final int ED_KEY_PAIR_BITS = 256;
|
||||
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_TARGET_MILLIS = 500;
|
||||
@@ -99,6 +104,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||
private final MessageEncrypter messageEncrypter;
|
||||
private final KeyPairGenerator edKeyPairGenerator;
|
||||
private final KeyParser edKeyParser;
|
||||
|
||||
@Inject
|
||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||
@@ -132,6 +139,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||
SIGNATURE_KEY_PAIR_BITS);
|
||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||
edKeyPairGenerator = new KeyPairGenerator();
|
||||
edKeyPairGenerator.initialize(ED_KEY_PAIR_BITS, secureRandom);
|
||||
edKeyParser = new EdKeyParser();
|
||||
}
|
||||
|
||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||
@@ -190,6 +200,21 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return secret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateEdKeyPair() {
|
||||
java.security.KeyPair keyPair = edKeyPairGenerator.generateKeyPair();
|
||||
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyParser getEdKeyParser() {
|
||||
return edKeyParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateAgreementKeyPair() {
|
||||
AsymmetricCipherKeyPair keyPair =
|
||||
@@ -416,19 +441,41 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
@Override
|
||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException {
|
||||
Signature signature = new SignatureImpl(secureRandom);
|
||||
KeyParser keyParser = getSignatureKeyParser();
|
||||
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
|
||||
toSign, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException {
|
||||
return sign(new EdSignature(), edKeyParser, label, toSign, privateKey);
|
||||
}
|
||||
|
||||
private byte[] sign(Signature sig, KeyParser keyParser, String label,
|
||||
byte[] toSign, byte[] privateKey) throws GeneralSecurityException {
|
||||
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
||||
signature.initSign(key);
|
||||
updateSignature(signature, label, toSign);
|
||||
return signature.sign();
|
||||
sig.initSign(key);
|
||||
updateSignature(sig, label, toSign);
|
||||
return sig.sign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
byte[] signature) throws GeneralSecurityException {
|
||||
Signature sig = new SignatureImpl(secureRandom);
|
||||
KeyParser keyParser = getSignatureKeyParser();
|
||||
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
|
||||
label, signedData, publicKey, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||
byte[] signature) throws GeneralSecurityException {
|
||||
return verify(new EdSignature(), edKeyParser, label, signedData,
|
||||
publicKey, signature);
|
||||
}
|
||||
|
||||
private boolean verify(Signature sig, KeyParser keyParser, String label,
|
||||
byte[] signedData, byte[] publicKey, byte[] signature)
|
||||
throws GeneralSecurityException {
|
||||
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||
sig.initVerify(key);
|
||||
updateSignature(sig, label, signedData);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
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 EdKeyParser implements KeyParser {
|
||||
|
||||
@Override
|
||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new EdPublicKey(encodedKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new EdPrivateKey(encodedKey);
|
||||
}
|
||||
}
|
||||
@@ -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 EdPrivateKey extends Bytes implements PrivateKey {
|
||||
|
||||
EdPrivateKey(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 EdPublicKey extends Bytes implements PublicKey {
|
||||
|
||||
EdPublicKey(byte[] bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
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;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||
|
||||
@NotNullByDefault
|
||||
class EdSignature implements Signature {
|
||||
|
||||
private static final Provider PROVIDER = new EdDSASecurityProvider();
|
||||
|
||||
private static final EdDSANamedCurveSpec CURVE_SPEC =
|
||||
EdDSANamedCurveTable.getByName("Ed25519");
|
||||
|
||||
private final java.security.Signature signature;
|
||||
|
||||
EdSignature() {
|
||||
try {
|
||||
signature = java.security.Signature
|
||||
.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||
if (!(k instanceof EdPrivateKey))
|
||||
throw new IllegalArgumentException();
|
||||
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
||||
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||
signature.initSign(privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||
if (!(k instanceof EdPublicKey))
|
||||
throw new IllegalArgumentException();
|
||||
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
||||
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||
signature.initVerify(publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte b) {
|
||||
try {
|
||||
signature.update(b);
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] b) {
|
||||
try {
|
||||
signature.update(b);
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] b, int off, int len) {
|
||||
try {
|
||||
signature.update(b, off, len);
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign() {
|
||||
try {
|
||||
return signature.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(byte[] sig) {
|
||||
try {
|
||||
return signature.verify(sig);
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public class EcdsaSignatureTest extends SignatureTest {
|
||||
|
||||
@Override
|
||||
protected KeyPair generateKeyPair() {
|
||||
return crypto.generateSignatureKeyPair();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException {
|
||||
return crypto.sign(label, toSign, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
byte[] signature) throws GeneralSecurityException {
|
||||
return crypto.verify(label, signedData, publicKey, signature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public class EdSignatureTest extends SignatureTest {
|
||||
|
||||
@Override
|
||||
protected KeyPair generateKeyPair() {
|
||||
return crypto.generateEdKeyPair();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
throws GeneralSecurityException {
|
||||
return crypto.signEd(label, toSign, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||
byte[] signature) throws GeneralSecurityException {
|
||||
return crypto.verifyEd(label, signedData, publicKey, signature);
|
||||
}
|
||||
}
|
||||
@@ -8,23 +8,32 @@ import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SignatureTest extends BrambleTestCase {
|
||||
public abstract class SignatureTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
protected final CryptoComponent crypto;
|
||||
|
||||
private final byte[] publicKey, privateKey;
|
||||
private final String label = StringUtils.getRandomString(42);
|
||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||
|
||||
public SignatureTest() {
|
||||
protected abstract KeyPair generateKeyPair();
|
||||
|
||||
protected abstract byte[] sign(String label, byte[] toSign,
|
||||
byte[] privateKey) throws GeneralSecurityException;
|
||||
|
||||
protected abstract boolean verify(String label, byte[] signedData,
|
||||
byte[] publicKey, byte[] signature) throws GeneralSecurityException;
|
||||
|
||||
SignatureTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
KeyPair k = crypto.generateSignatureKeyPair();
|
||||
KeyPair k = generateKeyPair();
|
||||
publicKey = k.getPublic().getEncoded();
|
||||
privateKey = k.getPrivate().getEncoded();
|
||||
}
|
||||
@@ -33,19 +42,19 @@ public class SignatureTest extends BrambleTestCase {
|
||||
public void testIdenticalKeysAndInputsProduceIdenticalSignatures()
|
||||
throws Exception {
|
||||
// Calculate the Signature twice - the results should be identical
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes, privateKey);
|
||||
assertArrayEquals(sig1, sig2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeysProduceDifferentSignatures() throws Exception {
|
||||
// Generate second private key
|
||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
||||
KeyPair k2 = generateKeyPair();
|
||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||
// Calculate the signature with each key
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes, privateKey2);
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes, privateKey2);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@@ -56,8 +65,8 @@ public class SignatureTest extends BrambleTestCase {
|
||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||
// Calculate the signature with different inputs
|
||||
// the results should be different
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label, inputBytes2, privateKey);
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label, inputBytes2, privateKey);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@@ -68,25 +77,25 @@ public class SignatureTest extends BrambleTestCase {
|
||||
String label2 = StringUtils.getRandomString(42);
|
||||
// Calculate the signature with different inputs
|
||||
// the results should be different
|
||||
byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = crypto.sign(label2, inputBytes, privateKey);
|
||||
byte[] sig1 = sign(label, inputBytes, privateKey);
|
||||
byte[] sig2 = sign(label2, inputBytes, privateKey);
|
||||
assertFalse(Arrays.equals(sig1, sig2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignatureVerification() throws Exception {
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertTrue(crypto.verify(label, inputBytes, publicKey, sig));
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertTrue(verify(label, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentKeyFailsVerification() throws Exception {
|
||||
// Generate second private key
|
||||
KeyPair k2 = crypto.generateSignatureKeyPair();
|
||||
KeyPair k2 = generateKeyPair();
|
||||
byte[] privateKey2 = k2.getPrivate().getEncoded();
|
||||
// calculate the signature with different key, should fail to verify
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey2);
|
||||
assertFalse(crypto.verify(label, inputBytes, publicKey, sig));
|
||||
byte[] sig = sign(label, inputBytes, privateKey2);
|
||||
assertFalse(verify(label, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,8 +103,8 @@ public class SignatureTest extends BrambleTestCase {
|
||||
// Generate a second input
|
||||
byte[] inputBytes2 = TestUtils.getRandomBytes(123);
|
||||
// calculate the signature with different input, should fail to verify
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertFalse(crypto.verify(label, inputBytes2, publicKey, sig));
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertFalse(verify(label, inputBytes2, publicKey, sig));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,8 +112,8 @@ public class SignatureTest extends BrambleTestCase {
|
||||
// Generate a second label
|
||||
String label2 = StringUtils.getRandomString(42);
|
||||
// calculate the signature with different label, should fail to verify
|
||||
byte[] sig = crypto.sign(label, inputBytes, privateKey);
|
||||
assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));
|
||||
byte[] sig = sign(label, inputBytes, privateKey);
|
||||
assertFalse(verify(label2, inputBytes, publicKey, sig));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user