From 77e4ec381ac7600e8233d48ec6dd7b3f6dd81fec Mon Sep 17 00:00:00 2001
From: str4d <str4d@mail.i2p>
Date: Thu, 28 Jan 2016 06:53:20 +0000
Subject: [PATCH] Implement BQP crypto

---
 .../api/crypto/CryptoComponent.java           | 54 +++++++++++++++++
 .../keyagreement/KeyAgreementConstants.java   |  8 +++
 .../crypto/CryptoComponentImpl.java           | 59 +++++++++++++++++++
 .../briarproject/crypto/KeyAgreementTest.java | 13 ++++
 4 files changed, 134 insertions(+)
 create mode 100644 briar-api/src/org/briarproject/api/keyagreement/KeyAgreementConstants.java

diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
index 65b12c961c..ac0842472c 100644
--- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
+++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
@@ -57,6 +57,60 @@ public interface CryptoComponent {
 	 */
 	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 initial transport keys for the given transport in the given
 	 * rotation period from the given master secret.
diff --git a/briar-api/src/org/briarproject/api/keyagreement/KeyAgreementConstants.java b/briar-api/src/org/briarproject/api/keyagreement/KeyAgreementConstants.java
new file mode 100644
index 0000000000..521789244d
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/keyagreement/KeyAgreementConstants.java
@@ -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;
+}
diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
index 79759d995b..07916ab5ba 100644
--- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
@@ -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;
@@ -73,6 +74,14 @@ class CryptoComponentImpl implements CryptoComponent {
 	// 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");
@@ -231,6 +240,56 @@ class CryptoComponentImpl implements CryptoComponent {
 		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 TransportKeys deriveTransportKeys(TransportId t,
 			SecretKey master, long rotationPeriod, boolean alice) {
 		// Keys for the previous period are derived from the master secret
diff --git a/briar-tests/src/org/briarproject/crypto/KeyAgreementTest.java b/briar-tests/src/org/briarproject/crypto/KeyAgreementTest.java
index 1544638c32..ff04f23d68 100644
--- a/briar-tests/src/org/briarproject/crypto/KeyAgreementTest.java
+++ b/briar-tests/src/org/briarproject/crypto/KeyAgreementTest.java
@@ -24,4 +24,17 @@ public class KeyAgreementTest extends BriarTestCase {
 		SecretKey bMaster = crypto.deriveBTMasterSecret(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());
+	}
 }
-- 
GitLab