From 2dd369214e95da6ca0cbc9056f547a155286b9ac Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Thu, 3 Jan 2013 21:37:42 +0000
Subject: [PATCH] Use NIST curve P-384 and the SEC 1 encoding for public keys.

---
 .../sf/briar/crypto/CryptoComponentImpl.java  | 88 +++++++++++++++++--
 .../net/sf/briar/crypto/KeyParserImpl.java    | 28 ------
 .../net/sf/briar/crypto/Sec1KeyParser.java    | 56 ++++++++++++
 .../net/sf/briar/crypto/Sec1PublicKey.java    | 55 ++++++++++++
 4 files changed, 192 insertions(+), 35 deletions(-)
 delete mode 100644 briar-core/src/net/sf/briar/crypto/KeyParserImpl.java
 create mode 100644 briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
 create mode 100644 briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java

diff --git a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
index 8783c0ca8d..bae1e7de85 100644
--- a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -5,13 +5,21 @@ import static net.sf.briar.api.plugins.InvitationConstants.CODE_BITS;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
+import java.math.BigInteger;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.Signature;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyAgreement;
@@ -69,6 +77,41 @@ class CryptoComponentImpl implements CryptoComponent {
 	private static final byte[] KEY_DERIVATION_BLANK_PLAINTEXT =
 			new byte[SECRET_KEY_BYTES];
 
+	// Parameters for NIST elliptic curve P-384 - see "Suite B Implementer's
+	// Guide to NIST SP 800-56A", section A.2
+	private static final BigInteger P_384_Q = new BigInteger("FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
+			"FFFFFFFF", 16);
+	private static final BigInteger P_384_A = new BigInteger("FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
+			"FFFFFFFC", 16);
+	private static final BigInteger P_384_B = new BigInteger("B3312FA7" +
+			"E23EE7E4" + "988E056B" + "E3F82D19" + "181D9C6E" + "FE814112" +
+			"0314088F" + "5013875A" + "C656398D" + "8A2ED19D" + "2A85C8ED" +
+			"D3EC2AEF", 16);
+	private static final BigInteger P_384_G_X = new BigInteger("AA87CA22" +
+			"BE8B0537" + "8EB1C71E" + "F320AD74" + "6E1D3B62" + "8BA79B98" +
+			"59F741E0" + "82542A38" + "5502F25D" + "BF55296C" + "3A545E38" +
+			"72760AB7", 16);
+	private static final BigInteger P_384_G_Y = new BigInteger("3617DE4A" +
+			"96262C6F" + "5D9E98BF" + "9292DC29" + "F8F41DBD" + "289A147C" +
+			"E9DA3113" + "B5F0B8C0" + "0A60B1CE" + "1D7E819D" + "7A431D7C" +
+			"90EA0E5F", 16);
+	private static final BigInteger P_384_N = new BigInteger("FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"C7634D81" + "F4372DDF" + "581A0DB2" + "48B0A77A" + "ECEC196A" +
+			"CCC52973", 16);
+	private static final int P_384_H = 1;
+	// Static parameter objects derived from the above parameters
+	private static final ECField P_384_FIELD = new ECFieldFp(P_384_Q);
+	private static final EllipticCurve P_384_CURVE =
+			new EllipticCurve(P_384_FIELD, P_384_A, P_384_B);
+	private static final ECPoint P_384_G = new ECPoint(P_384_G_X, P_384_G_Y);
+	private static final ECParameterSpec P_384_PARAMS =
+			new ECParameterSpec(P_384_CURVE, P_384_G, P_384_N, P_384_H);
+
 	private final KeyParser agreementKeyParser, signatureKeyParser;
 	private final KeyPairGenerator agreementKeyPairGenerator;
 	private final KeyPairGenerator signatureKeyPairGenerator;
@@ -78,10 +121,14 @@ class CryptoComponentImpl implements CryptoComponent {
 	CryptoComponentImpl() {
 		Security.addProvider(new BouncyCastleProvider());
 		try {
-			agreementKeyParser = new KeyParserImpl(AGREEMENT_KEY_PAIR_ALGO,
-					PROVIDER);
-			signatureKeyParser = new KeyParserImpl(SIGNATURE_KEY_PAIR_ALGO,
-					PROVIDER);
+			KeyFactory agreementKeyFactory = KeyFactory.getInstance(
+					AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
+			agreementKeyParser = new Sec1KeyParser(agreementKeyFactory,
+					P_384_PARAMS, P_384_Q, AGREEMENT_KEY_PAIR_BITS);
+			KeyFactory signatureKeyFactory = KeyFactory.getInstance(
+					SIGNATURE_KEY_PAIR_ALGO, PROVIDER);
+			signatureKeyParser = new Sec1KeyParser(signatureKeyFactory,
+					P_384_PARAMS, P_384_Q, SIGNATURE_KEY_PAIR_BITS);
 			agreementKeyPairGenerator = KeyPairGenerator.getInstance(
 					AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
 			agreementKeyPairGenerator.initialize(AGREEMENT_KEY_PAIR_BITS);
@@ -243,7 +290,29 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	public KeyPair generateAgreementKeyPair() {
-		return agreementKeyPairGenerator.generateKeyPair();
+		KeyPair keyPair = agreementKeyPairGenerator.generateKeyPair();
+		// Check that the key pair uses NIST curve P-384
+		ECPublicKey ecPublicKey = checkP384Params(keyPair.getPublic());
+		// Return a public key that uses the SEC 1 encoding
+		ecPublicKey = new Sec1PublicKey(ecPublicKey, AGREEMENT_KEY_PAIR_BITS);
+		return new KeyPair(ecPublicKey, keyPair.getPrivate());
+	}
+
+	private ECPublicKey checkP384Params(PublicKey publicKey) {
+		if(!(publicKey instanceof ECPublicKey)) throw new RuntimeException();
+		ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
+		ECParameterSpec params = ecPublicKey.getParams();
+		EllipticCurve curve = params.getCurve();
+		ECField field = curve.getField();
+		if(!(field instanceof ECFieldFp)) throw new RuntimeException();
+		BigInteger q = ((ECFieldFp) field).getP();
+		if(!q.equals(P_384_Q)) throw new RuntimeException();
+		if(!curve.getA().equals(P_384_A)) throw new RuntimeException();
+		if(!curve.getB().equals(P_384_B)) throw new RuntimeException();
+		if(!params.getGenerator().equals(P_384_G)) throw new RuntimeException();
+		if(!params.getOrder().equals(P_384_N)) throw new RuntimeException();
+		if(!(params.getCofactor() == P_384_H)) throw new RuntimeException();
+		return ecPublicKey;
 	}
 
 	public KeyParser getAgreementKeyParser() {
@@ -251,7 +320,12 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	public KeyPair generateSignatureKeyPair() {
-		return signatureKeyPairGenerator.generateKeyPair();
+		KeyPair keyPair = signatureKeyPairGenerator.generateKeyPair();
+		// Check that the key pair uses NIST curve P-384
+		ECPublicKey ecPublicKey = checkP384Params(keyPair.getPublic());
+		// Return a public key that uses the SEC 1 encoding
+		ecPublicKey = new Sec1PublicKey(ecPublicKey, SIGNATURE_KEY_PAIR_BITS);
+		return new KeyPair(ecPublicKey, keyPair.getPrivate());
 	}
 
 	public KeyParser getSignatureKeyParser() {
@@ -260,7 +334,7 @@ class CryptoComponentImpl implements CryptoComponent {
 
 	public ErasableKey generateTestKey() {
 		byte[] b = new byte[SECRET_KEY_BYTES];
-		getSecureRandom().nextBytes(b);
+		secureRandom.nextBytes(b);
 		return new ErasableKeyImpl(b, SECRET_KEY_ALGO);
 	}
 
diff --git a/briar-core/src/net/sf/briar/crypto/KeyParserImpl.java b/briar-core/src/net/sf/briar/crypto/KeyParserImpl.java
deleted file mode 100644
index ec875f60d4..0000000000
--- a/briar-core/src/net/sf/briar/crypto/KeyParserImpl.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.sf.briar.crypto;
-
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PublicKey;
-import java.security.spec.EncodedKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-
-import net.sf.briar.api.crypto.KeyParser;
-
-class KeyParserImpl implements KeyParser {
-
-	private final KeyFactory keyFactory;
-
-	KeyParserImpl(String algorithm, String provider)
-	throws NoSuchAlgorithmException, NoSuchProviderException {
-		keyFactory = KeyFactory.getInstance(algorithm, provider);
-	}
-
-	public PublicKey parsePublicKey(byte[] encodedKey)
-	throws InvalidKeySpecException {
-		EncodedKeySpec e = new X509EncodedKeySpec(encodedKey);
-		return keyFactory.generatePublic(e);
-	}
-
-}
diff --git a/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java b/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
new file mode 100644
index 0000000000..87a3e7a6fa
--- /dev/null
+++ b/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
@@ -0,0 +1,56 @@
+package net.sf.briar.crypto;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import net.sf.briar.api.crypto.KeyParser;
+
+/**
+ * A key parser that uses the encoding defined in "SEC 1: Elliptic Curve
+ * Cryptography", section 2.3 (Certicom Corporation, May 2009). Point
+ * compression is not used.
+ */
+class Sec1KeyParser implements KeyParser {
+
+	private final KeyFactory keyFactory;
+	private final ECParameterSpec params;
+	private final BigInteger modulus;
+	private final int bytesPerInt, encodedKeyLength;
+
+	Sec1KeyParser(KeyFactory keyFactory, ECParameterSpec params,
+			BigInteger modulus, int keyBits) {
+		this.keyFactory = keyFactory;
+		this.params = params;
+		this.modulus = modulus;
+		bytesPerInt = (int) Math.ceil(keyBits / 8.0);
+		encodedKeyLength = 1 + 2 * bytesPerInt;
+	}
+
+	public PublicKey parsePublicKey(byte[] encodedKey)
+			throws InvalidKeySpecException {
+		if(encodedKey.length != encodedKeyLength)
+			throw new InvalidKeySpecException();
+		// The first byte must be 0x04
+		if(encodedKey[0] != 4) throw new InvalidKeySpecException();
+		// The x co-ordinate must be >= 0 and < q
+		byte[] xBytes = new byte[bytesPerInt];
+		System.arraycopy(encodedKey, 1, xBytes, 0, bytesPerInt);
+		BigInteger x = new BigInteger(1, xBytes); // Positive signum
+		if(x.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
+		// The y co-ordinate must be >= 0 and < q
+		byte[] yBytes = new byte[bytesPerInt];
+		System.arraycopy(encodedKey, bytesPerInt + 1, yBytes, 0, bytesPerInt);
+		BigInteger y = new BigInteger(1, yBytes); // Positive signum
+		if(y.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
+		// FIXME: Verify that y^2 == x^3 + ax + b (mod q)
+		// Construct a public key from the point (x, y) and the params
+		ECPoint pub = new ECPoint(x, y);
+		ECPublicKeySpec keySpec = new ECPublicKeySpec(pub, params);
+		return keyFactory.generatePublic(keySpec);
+	}
+}
diff --git a/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java b/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java
new file mode 100644
index 0000000000..10170e5393
--- /dev/null
+++ b/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java
@@ -0,0 +1,55 @@
+package net.sf.briar.crypto;
+
+import java.math.BigInteger;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+
+/**
+ * An elliptic curve public key that uses the encoding defined in "SEC 1:
+ * Elliptic Curve Cryptography", section 2.3 (Certicom Corporation, May 2009).
+ * Point compression is not used.
+ */
+class Sec1PublicKey implements ECPublicKey {
+
+	private static final long serialVersionUID = -2722797033851423987L;
+
+	private final ECPublicKey key;
+	private final int bytesPerInt, encodedKeyLength;
+
+	Sec1PublicKey(ECPublicKey key, int keyBits) {
+		this.key = key;
+		bytesPerInt = (int) Math.ceil(keyBits / 8.0);
+		encodedKeyLength = 1 + 2 * bytesPerInt;
+	}
+
+	public String getAlgorithm() {
+		return key.getAlgorithm();
+	}
+
+	public byte[] getEncoded() {
+		byte[] encodedKey = new byte[encodedKeyLength];
+		encodedKey[0] = 4;
+		BigInteger x = key.getW().getAffineX(), y = key.getW().getAffineY();
+		// Copy up to bytesPerInt bytes into exactly bytesPerInt bytes
+		byte[] xBytes = x.toByteArray();
+		for(int i = 0; i < xBytes.length && i < bytesPerInt; i++)
+			encodedKey[bytesPerInt - i] = xBytes[xBytes.length - 1 - i];
+		byte[] yBytes = y.toByteArray();
+		for(int i = 0; i < yBytes.length && i < bytesPerInt; i++)
+			encodedKey[2 * bytesPerInt - i] = yBytes[yBytes.length - 1 - i];
+		return encodedKey;
+	}
+
+	public String getFormat() {
+		return "SEC1";
+	}
+
+	public ECParameterSpec getParams() {
+		return key.getParams();
+	}
+
+	public ECPoint getW() {
+		return key.getW();
+	}
+}
-- 
GitLab