Merge branch '117-bqp-crypto' into 'master'

BQP crypto

This implements the `CryptoComponent` methods necessary for BQP.

Part of #117.

See merge request !88
This commit is contained in:
akwizgran
2016-02-02 15:34:26 +00:00
8 changed files with 219 additions and 93 deletions

View File

@@ -244,7 +244,7 @@ implements InvitationListener {
int getLocalInvitationCode() {
if (localInvitationCode == -1)
localInvitationCode = crypto.generateInvitationCode();
localInvitationCode = crypto.generateBTInvitationCode();
return localInvitationCode;
}

View File

@@ -27,35 +27,97 @@ public interface CryptoComponent {
KeyParser getSignatureKeyParser();
/** Generates a random invitation code. */
int generateInvitationCode();
/**
* Derives a shared master secret from two public keys and one of the
* corresponding private keys.
* @param alice whether the private key belongs to Alice or Bob.
*/
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
int generateBTInvitationCode();
/**
* Derives a confirmation code from the given master secret.
* @param alice whether the code is for use by Alice or Bob.
*/
int deriveConfirmationCode(SecretKey master, boolean alice);
int deriveBTConfirmationCode(SecretKey master, boolean alice);
/**
* Derives a header key for an invitation stream from the given master
* secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveInvitationKey(SecretKey master, boolean alice);
SecretKey deriveBTInvitationKey(SecretKey master, boolean alice);
/**
* Derives a nonce from the given master secret for one of the parties to
* sign.
* @param alice whether the nonce is for use by Alice or Bob.
*/
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
byte[] deriveBTSignatureNonce(SecretKey master, boolean alice);
/**
* Derives a commitment to the provided public key.
* <p/>
* Part of BQP.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(byte[] publicKey);
/**
* Derives a common shared secret from two public keys and one of the
* corresponding private keys.
* <p/>
* Part of BQP.
*
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Derives the content of a confirmation record.
* <p/>
* Part of BQP.
*
* @param sharedSecret the common shared secret
* @param theirPayload the commit payload from the remote party
* @param ourPayload the commit payload we sent
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
/**
* Derives a master secret from the given shared secret.
* <p/>
* Part of BQP.
*
* @param sharedSecret the common shared secret
* @return the master secret
*/
SecretKey deriveMasterSecret(SecretKey sharedSecret);
/**
* Derives a master secret from two public keys and one of the corresponding
* private keys.
* <p/>
* Part of BQP. This is a helper method that calls
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
*
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Derives initial transport keys for the given transport in the given

View File

@@ -0,0 +1,8 @@
package org.briarproject.api.keyagreement;
public interface KeyAgreementConstants {
/** The length of the BQP key commitment in bytes. */
int COMMIT_LENGTH = 16;
}

View File

@@ -40,6 +40,7 @@ import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static org.briarproject.api.invitation.InvitationConstants.CODE_BITS;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.util.ByteUtils.INT_32_BYTES;
@@ -62,17 +63,25 @@ class CryptoComponentImpl implements CryptoComponent {
return s.getBytes(Charset.forName("US-ASCII"));
}
// KDF label for master key derivation
private static final byte[] MASTER = ascii("MASTER");
// KDF labels for confirmation code derivation
private static final byte[] A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
private static final byte[] B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
// KDF labels for invitation stream header key derivation
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
// KDF labels for signature nonce derivation
private static final byte[] A_NONCE = ascii("ALICE_SIGNATURE_NONCE");
private static final byte[] B_NONCE = ascii("BOB_SIGNATURE_NONCE");
// KDF label for bluetooth master key derivation
private static final byte[] BT_MASTER = ascii("MASTER");
// KDF labels for bluetooth confirmation code derivation
private static final byte[] BT_A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
private static final byte[] BT_B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
// KDF labels for bluetooth invitation stream header key derivation
private static final byte[] BT_A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] BT_B_INVITE = ascii("BOB_INVITATION_KEY");
// KDF labels for bluetooth signature nonce derivation
private static final byte[] BT_A_NONCE = ascii("ALICE_SIGNATURE_NONCE");
private static final byte[] BT_B_NONCE = ascii("BOB_SIGNATURE_NONCE");
// Hash label for BQP public key commitment derivation
private static final byte[] COMMIT = ascii("COMMIT");
// Hash label for BQP shared secret derivation
private static final byte[] SHARED_SECRET = ascii("SHARED_SECRET");
// KDF label for BQP confirmation key derivation
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
// KDF label for BQP master key derivation
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
// KDF labels for tag key derivation
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
@@ -128,6 +137,25 @@ class CryptoComponentImpl implements CryptoComponent {
return secureRandom;
}
// Package access for testing
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
throws GeneralSecurityException {
if (!(priv instanceof Sec1PrivateKey))
throw new IllegalArgumentException();
if (!(pub instanceof Sec1PublicKey))
throw new IllegalArgumentException();
ECPrivateKeyParameters ecPriv = ((Sec1PrivateKey) priv).getKey();
ECPublicKeyParameters ecPub = ((Sec1PublicKey) pub).getKey();
long now = System.currentTimeMillis();
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
agreement.init(ecPriv);
byte[] secret = agreement.calculateAgreement(ecPub).toByteArray();
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Deriving shared secret took " + duration + " ms");
return secret;
}
public Signature getSignature() {
return new SignatureImpl(secureRandom);
}
@@ -170,65 +198,80 @@ class CryptoComponentImpl implements CryptoComponent {
return signatureKeyParser;
}
public int generateInvitationCode() {
public int generateBTInvitationCode() {
int codeBytes = (CODE_BITS + 7) / 8;
byte[] random = new byte[codeBytes];
secureRandom.nextBytes(random);
return ByteUtils.readUint(random, CODE_BITS);
}
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
MessageDigest messageDigest = getMessageDigest();
byte[] ourPublicKey = ourKeyPair.getPublic().getEncoded();
byte[] ourHash = messageDigest.digest(ourPublicKey);
byte[] theirHash = messageDigest.digest(theirPublicKey);
byte[] aliceInfo, bobInfo;
if (alice) {
aliceInfo = ourHash;
bobInfo = theirHash;
} else {
aliceInfo = theirHash;
bobInfo = ourHash;
}
PrivateKey ourPriv = ourKeyPair.getPrivate();
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
// The raw secret comes from the key agreement algorithm
byte[] raw = deriveSharedSecret(ourPriv, theirPub);
// Derive the master secret from the raw secret using the hash KDF
return new SecretKey(hashKdf(raw, MASTER, aliceInfo, bobInfo));
}
// Package access for testing
byte[] deriveSharedSecret(PrivateKey priv, PublicKey pub)
throws GeneralSecurityException {
if (!(priv instanceof Sec1PrivateKey))
throw new IllegalArgumentException();
if (!(pub instanceof Sec1PublicKey))
throw new IllegalArgumentException();
ECPrivateKeyParameters ecPriv = ((Sec1PrivateKey) priv).getKey();
ECPublicKeyParameters ecPub = ((Sec1PublicKey) pub).getKey();
long now = System.currentTimeMillis();
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
agreement.init(ecPriv);
byte[] secret = agreement.calculateAgreement(ecPub).toByteArray();
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Deriving shared secret took " + duration + " ms");
return secret;
}
public int deriveConfirmationCode(SecretKey master, boolean alice) {
byte[] b = macKdf(master, alice ? A_CONFIRM : B_CONFIRM);
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
return ByteUtils.readUint(b, CODE_BITS);
}
public SecretKey deriveInvitationKey(SecretKey master, boolean alice) {
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
public SecretKey deriveBTInvitationKey(SecretKey master, boolean alice) {
return new SecretKey(macKdf(master, alice ? BT_A_INVITE : BT_B_INVITE));
}
public byte[] deriveSignatureNonce(SecretKey master, boolean alice) {
return macKdf(master, alice ? A_NONCE : B_NONCE);
public byte[] deriveBTSignatureNonce(SecretKey master, boolean alice) {
return macKdf(master, alice ? BT_A_NONCE : BT_B_NONCE);
}
public byte[] deriveKeyCommitment(byte[] publicKey) {
byte[] hash = hash(COMMIT, publicKey);
// The output is the first COMMIT_LENGTH bytes of the hash
byte[] commitment = new byte[COMMIT_LENGTH];
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
return commitment;
}
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
PrivateKey ourPriv = ourKeyPair.getPrivate();
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
byte[] alicePub, bobPub;
if (alice) {
alicePub = ourKeyPair.getPublic().getEncoded();
bobPub = theirPublicKey;
} else {
alicePub = theirPublicKey;
bobPub = ourKeyPair.getPublic().getEncoded();
}
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
}
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
byte[] alicePayload, alicePub, bobPayload, bobPub;
if (alice) {
alicePayload = ourPayload;
alicePub = ourKeyPair.getPublic().getEncoded();
bobPayload = theirPayload;
bobPub = theirPublicKey;
} else {
alicePayload = theirPayload;
alicePub = theirPublicKey;
bobPayload = ourPayload;
bobPub = ourKeyPair.getPublic().getEncoded();
}
if (aliceRecord)
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
else
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
}
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
}
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
return deriveMasterSecret(deriveSharedSecret(
theirPublicKey,ourKeyPair, alice));
}
public TransportKeys deriveTransportKeys(TransportId t,

View File

@@ -93,8 +93,8 @@ class AliceConnector extends Connector {
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveConfirmationCode(master, true);
int bobCode = crypto.deriveConfirmationCode(master, false);
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(aliceCode, bobCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
@@ -128,8 +128,8 @@ class AliceConnector extends Connector {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
// Derive the header keys
SecretKey aliceHeaderKey = crypto.deriveInvitationKey(master, true);
SecretKey bobHeaderKey = crypto.deriveInvitationKey(master, false);
SecretKey aliceHeaderKey = crypto.deriveBTInvitationKey(master, true);
SecretKey bobHeaderKey = crypto.deriveBTInvitationKey(master, false);
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(in,
@@ -141,8 +141,8 @@ class AliceConnector extends Connector {
aliceHeaderKey);
w = bdfWriterFactory.createWriter(streamWriter);
// Derive the invitation nonces
byte[] aliceNonce = crypto.deriveSignatureNonce(master, true);
byte[] bobNonce = crypto.deriveSignatureNonce(master, false);
byte[] aliceNonce = crypto.deriveBTSignatureNonce(master, true);
byte[] bobNonce = crypto.deriveBTSignatureNonce(master, false);
// Exchange pseudonyms, signed nonces, and timestamps
Author remoteAuthor;
long remoteTimestamp;

View File

@@ -93,8 +93,8 @@ class BobConnector extends Connector {
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveConfirmationCode(master, true);
int bobCode = crypto.deriveConfirmationCode(master, false);
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(bobCode, aliceCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
@@ -128,8 +128,8 @@ class BobConnector extends Connector {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
// Derive the header keys
SecretKey aliceHeaderKey = crypto.deriveInvitationKey(master, true);
SecretKey bobHeaderKey = crypto.deriveInvitationKey(master, false);
SecretKey aliceHeaderKey = crypto.deriveBTInvitationKey(master, true);
SecretKey bobHeaderKey = crypto.deriveBTInvitationKey(master, false);
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(in,
@@ -141,8 +141,8 @@ class BobConnector extends Connector {
bobHeaderKey);
w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces
byte[] aliceNonce = crypto.deriveSignatureNonce(master, true);
byte[] bobNonce = crypto.deriveSignatureNonce(master, false);
byte[] aliceNonce = crypto.deriveBTSignatureNonce(master, true);
byte[] bobNonce = crypto.deriveBTSignatureNonce(master, false);
// Exchange pseudonyms, signed nonces and timestamps
Author remoteAuthor;
long remoteTimestamp;

View File

@@ -13,7 +13,7 @@ import static org.junit.Assert.assertArrayEquals;
public class KeyAgreementTest extends BriarTestCase {
@Test
public void testKeyAgreement() throws Exception {
public void testBTKeyAgreement() throws Exception {
SeedProvider seedProvider = new TestSeedProvider();
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
KeyPair aPair = crypto.generateAgreementKeyPair();
@@ -24,4 +24,17 @@ public class KeyAgreementTest extends BriarTestCase {
SecretKey bMaster = crypto.deriveMasterSecret(bPub, aPair, false);
assertArrayEquals(aMaster.getBytes(), bMaster.getBytes());
}
@Test
public void testKeyAgreement() throws Exception {
SeedProvider seedProvider = new TestSeedProvider();
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
KeyPair aPair = crypto.generateAgreementKeyPair();
byte[] aPub = aPair.getPublic().getEncoded();
KeyPair bPair = crypto.generateAgreementKeyPair();
byte[] bPub = bPair.getPublic().getEncoded();
SecretKey aShared = crypto.deriveSharedSecret(bPub, aPair, true);
SecretKey bShared = crypto.deriveSharedSecret(aPub, bPair, false);
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
}
}

View File

@@ -27,12 +27,12 @@ public class KeyEncodingAndParsingTest extends BriarTestCase {
KeyPair bPair = crypto.generateAgreementKeyPair();
// Derive the shared secret
PublicKey aPub = aPair.getPublic();
byte[] secret = crypto.deriveSharedSecret(bPair.getPrivate(), aPub);
byte[] secret = crypto.performRawKeyAgreement(bPair.getPrivate(), aPub);
// Encode and parse the public key - no exceptions should be thrown
aPub = parser.parsePublicKey(aPub.getEncoded());
aPub = parser.parsePublicKey(aPub.getEncoded());
// Derive the shared secret again - it should be the same
byte[] secret1 = crypto.deriveSharedSecret(bPair.getPrivate(), aPub);
byte[] secret1 = crypto.performRawKeyAgreement(bPair.getPrivate(), aPub);
assertArrayEquals(secret, secret1);
}
@@ -44,12 +44,12 @@ public class KeyEncodingAndParsingTest extends BriarTestCase {
KeyPair bPair = crypto.generateAgreementKeyPair();
// Derive the shared secret
PrivateKey bPriv = bPair.getPrivate();
byte[] secret = crypto.deriveSharedSecret(bPriv, aPair.getPublic());
byte[] secret = crypto.performRawKeyAgreement(bPriv, aPair.getPublic());
// Encode and parse the private key - no exceptions should be thrown
bPriv = parser.parsePrivateKey(bPriv.getEncoded());
bPriv = parser.parsePrivateKey(bPriv.getEncoded());
// Derive the shared secret again - it should be the same
byte[] secret1 = crypto.deriveSharedSecret(bPriv, aPair.getPublic());
byte[] secret1 = crypto.performRawKeyAgreement(bPriv, aPair.getPublic());
assertArrayEquals(secret, secret1);
}
@@ -90,12 +90,12 @@ public class KeyEncodingAndParsingTest extends BriarTestCase {
KeyPair bPair = crypto.generateSignatureKeyPair();
// Derive the shared secret
PublicKey aPub = aPair.getPublic();
byte[] secret = crypto.deriveSharedSecret(bPair.getPrivate(), aPub);
byte[] secret = crypto.performRawKeyAgreement(bPair.getPrivate(), aPub);
// Encode and parse the public key - no exceptions should be thrown
aPub = parser.parsePublicKey(aPub.getEncoded());
aPub = parser.parsePublicKey(aPub.getEncoded());
// Derive the shared secret again - it should be the same
byte[] secret1 = crypto.deriveSharedSecret(bPair.getPrivate(), aPub);
byte[] secret1 = crypto.performRawKeyAgreement(bPair.getPrivate(), aPub);
assertArrayEquals(secret, secret1);
}
@@ -107,12 +107,12 @@ public class KeyEncodingAndParsingTest extends BriarTestCase {
KeyPair bPair = crypto.generateSignatureKeyPair();
// Derive the shared secret
PrivateKey bPriv = bPair.getPrivate();
byte[] secret = crypto.deriveSharedSecret(bPriv, aPair.getPublic());
byte[] secret = crypto.performRawKeyAgreement(bPriv, aPair.getPublic());
// Encode and parse the private key - no exceptions should be thrown
bPriv = parser.parsePrivateKey(bPriv.getEncoded());
bPriv = parser.parsePrivateKey(bPriv.getEncoded());
// Derive the shared secret again - it should be the same
byte[] secret1 = crypto.deriveSharedSecret(bPriv, aPair.getPublic());
byte[] secret1 = crypto.performRawKeyAgreement(bPriv, aPair.getPublic());
assertArrayEquals(secret, secret1);
}