Separated key agreement algorithm from signature algorithm.

This commit is contained in:
akwizgran
2012-04-28 18:02:28 +01:00
parent b01b17f2b1
commit 5814826573
8 changed files with 87 additions and 27 deletions

View File

@@ -23,9 +23,11 @@ public interface CryptoComponent {
byte[] deriveNextSecret(byte[] secret, int index, long connection);
KeyPair generateKeyPair();
KeyPair generateAgreementKeyPair();
KeyParser getKeyParser();
KeyPair generateSignatureKeyPair();
KeyParser getSignatureKeyParser();
ErasableKey generateTestKey();

View File

@@ -30,14 +30,16 @@ import com.google.inject.Inject;
class CryptoComponentImpl implements CryptoComponent {
private static final String PROVIDER = "BC";
private static final String KEY_PAIR_ALGO = "ECDSA";
private static final int KEY_PAIR_BITS = 384;
private static final String KEY_AGREEMENT_ALGO = "ECDHC";
private static final String AGREEMENT_KEY_PAIR_ALGO = "ECDH";
private static final int AGREEMENT_KEY_PAIR_BITS = 384;
private static final String AGREEMENT_ALGO = "ECDHC";
private static final String SECRET_KEY_ALGO = "AES";
private static final int SECRET_KEY_BYTES = 32; // 256 bits
private static final int KEY_DERIVATION_IV_BYTES = 16; // 128 bits
private static final String KEY_DERIVATION_ALGO = "AES/CTR/NoPadding";
private static final String DIGEST_ALGO = "SHA-384";
private static final String SIGNATURE_KEY_PAIR_ALGO = "ECDSA";
private static final int SIGNATURE_KEY_PAIR_BITS = 384;
private static final String SIGNATURE_ALGO = "ECDSA";
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding";
@@ -59,18 +61,25 @@ class CryptoComponentImpl implements CryptoComponent {
private static final byte[] KEY_DERIVATION_INPUT =
new byte[SECRET_KEY_BYTES];
private final KeyParser keyParser;
private final KeyPairGenerator keyPairGenerator;
private final KeyParser agreementKeyParser, signatureKeyParser;
private final KeyPairGenerator agreementKeyPairGenerator;
private final KeyPairGenerator signatureKeyPairGenerator;
private final SecureRandom secureRandom;
@Inject
CryptoComponentImpl() {
Security.addProvider(new BouncyCastleProvider());
try {
keyParser = new KeyParserImpl(KEY_PAIR_ALGO, PROVIDER);
keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGO,
agreementKeyParser = new KeyParserImpl(AGREEMENT_KEY_PAIR_ALGO,
PROVIDER);
keyPairGenerator.initialize(KEY_PAIR_BITS);
signatureKeyParser = new KeyParserImpl(SIGNATURE_KEY_PAIR_ALGO,
PROVIDER);
agreementKeyPairGenerator = KeyPairGenerator.getInstance(
AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
agreementKeyPairGenerator.initialize(AGREEMENT_KEY_PAIR_BITS);
signatureKeyPairGenerator = KeyPairGenerator.getInstance(
SIGNATURE_KEY_PAIR_ALGO, PROVIDER);
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS);
} catch(GeneralSecurityException e) {
throw new RuntimeException(e);
}
@@ -130,7 +139,8 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] theirPublicKey, PrivateKey ourPrivateKey, int invitationCode,
boolean initiator) {
try {
PublicKey theirPublic = keyParser.parsePublicKey(theirPublicKey);
PublicKey theirPublic = agreementKeyParser.parsePublicKey(
theirPublicKey);
MessageDigest messageDigest = getMessageDigest();
byte[] ourHash = messageDigest.digest(ourPublicKey);
byte[] theirHash = messageDigest.digest(theirPublicKey);
@@ -147,8 +157,8 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] publicInfo = new byte[4];
ByteUtils.writeUint32(invitationCode, publicInfo, 0);
// The raw secret comes from the key agreement algorithm
KeyAgreement keyAgreement = KeyAgreement.getInstance(
KEY_AGREEMENT_ALGO, PROVIDER);
KeyAgreement keyAgreement = KeyAgreement.getInstance(AGREEMENT_ALGO,
PROVIDER);
keyAgreement.init(ourPrivateKey);
keyAgreement.doPhase(theirPublic, true);
byte[] rawSecret = keyAgreement.generateSecret();
@@ -220,12 +230,16 @@ class CryptoComponentImpl implements CryptoComponent {
return code;
}
public KeyPair generateKeyPair() {
return keyPairGenerator.generateKeyPair();
public KeyPair generateAgreementKeyPair() {
return agreementKeyPairGenerator.generateKeyPair();
}
public KeyParser getKeyParser() {
return keyParser;
public KeyPair generateSignatureKeyPair() {
return signatureKeyPairGenerator.generateKeyPair();
}
public KeyParser getSignatureKeyParser() {
return signatureKeyParser;
}
public ErasableKey generateTestKey() {

View File

@@ -106,7 +106,7 @@ class InvitationStarterImpl implements InvitationStarter {
return;
}
// Use an ephemeral key pair for key agreement
KeyPair ourKeyPair = crypto.generateKeyPair();
KeyPair ourKeyPair = crypto.generateAgreementKeyPair();
MessageDigest messageDigest = crypto.getMessageDigest();
byte[] ourKey = ourKeyPair.getPublic().getEncoded();
byte[] ourHash = messageDigest.digest(ourKey);

View File

@@ -57,7 +57,7 @@ class UnverifiedBatchImpl implements UnverifiedBatch {
// Verify the author's signature, if there is one
Author author = m.getAuthor();
if(author != null) {
if(keyParser == null) keyParser = crypto.getKeyParser();
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
PublicKey k = keyParser.parsePublicKey(author.getPublicKey());
if(signature == null) signature = crypto.getSignature();
signature.initVerify(k);
@@ -68,7 +68,7 @@ class UnverifiedBatchImpl implements UnverifiedBatch {
// Verify the group's signature, if there is one
Group group = m.getGroup();
if(group != null && group.getPublicKey() != null) {
if(keyParser == null) keyParser = crypto.getKeyParser();
if(keyParser == null) keyParser = crypto.getSignatureKeyParser();
PublicKey k = keyParser.parsePublicKey(group.getPublicKey());
if(signature == null) signature = crypto.getSignature();
signature.initVerify(k);

View File

@@ -100,12 +100,12 @@ public class ProtocolIntegrationTest extends BriarTestCase {
// Create two groups: one restricted, one unrestricted
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
group = groupFactory.createGroup("Unrestricted group", null);
KeyPair groupKeyPair = crypto.generateKeyPair();
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
group1 = groupFactory.createGroup("Restricted group",
groupKeyPair.getPublic().getEncoded());
// Create an author
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
KeyPair authorKeyPair = crypto.generateKeyPair();
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
author = authorFactory.createAuthor(authorName,
authorKeyPair.getPublic().getEncoded());
// Create two messages to each group: one anonymous, one pseudonymous

View File

@@ -0,0 +1,44 @@
package net.sf.briar.plugins;
import static org.junit.Assert.assertArrayEquals;
import java.security.KeyPair;
import java.security.PrivateKey;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.crypto.CryptoModule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class InvitationStarterImplTest extends BriarTestCase {
private final CryptoComponent crypto;
public InvitationStarterImplTest() {
super();
Injector i = Guice.createInjector(new CryptoModule());
crypto = i.getInstance(CryptoComponent.class);
}
@Test
public void testKeyAgreement() {
KeyPair a = crypto.generateAgreementKeyPair();
byte[] aPub = a.getPublic().getEncoded();
PrivateKey aPriv = a.getPrivate();
KeyPair b = crypto.generateAgreementKeyPair();
byte[] bPub = b.getPublic().getEncoded();
PrivateKey bPriv = b.getPrivate();
byte[][] aSecrets = crypto.deriveInitialSecrets(aPub, bPub, aPriv, 123,
true);
byte[][] bSecrets = crypto.deriveInitialSecrets(bPub, aPub, bPriv, 123,
false);
assertEquals(2, aSecrets.length);
assertEquals(2, bSecrets.length);
assertArrayEquals(aSecrets[0], bSecrets[0]);
assertArrayEquals(aSecrets[1], bSecrets[1]);
}
}

View File

@@ -108,8 +108,8 @@ public class ConstantsTest extends BriarTestCase {
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
Author author = authorFactory.createAuthor(authorName, authorPublic);
// Create a maximum-length message
PrivateKey groupPrivate = crypto.generateKeyPair().getPrivate();
PrivateKey authorPrivate = crypto.generateKeyPair().getPrivate();
PrivateKey groupPrivate = crypto.generateSignatureKeyPair().getPrivate();
PrivateKey authorPrivate = crypto.generateSignatureKeyPair().getPrivate();
String subject = createRandomString(MAX_SUBJECT_LENGTH);
byte[] body = new byte[MAX_BODY_LENGTH];
Message message = messageFactory.createMessage(null, group,

View File

@@ -122,8 +122,8 @@ public class UnverifiedBatchImplTest extends BriarTestCase {
@Test
public void testSignatures() throws Exception {
final int signedByAuthor = 100, signedByGroup = 110;
final KeyPair authorKeyPair = crypto.generateKeyPair();
final KeyPair groupKeyPair = crypto.generateKeyPair();
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
final KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
Signature signature = crypto.getSignature();
// Calculate the expected author and group signatures
signature.initSign(authorKeyPair.getPrivate());
@@ -202,7 +202,7 @@ public class UnverifiedBatchImplTest extends BriarTestCase {
@Test
public void testExceptionThrownIfMessageIsModified() throws Exception {
final int signedByAuthor = 100;
final KeyPair authorKeyPair = crypto.generateKeyPair();
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
Signature signature = crypto.getSignature();
// Calculate the expected author signature
signature.initSign(authorKeyPair.getPrivate());