From df972e294d857cd86da15b92ef179948e04de97a Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Thu, 11 Aug 2011 17:15:36 +0100
Subject: [PATCH] Support for decrypting shared secrets and deriving
 authentication and encryption keys from them (untested).

---
 .../sf/briar/api/crypto/CryptoComponent.java  |   6 +
 .../sf/briar/api/crypto/SecretStorageKey.java |  18 +++
 .../sf/briar/crypto/CryptoComponentImpl.java  | 135 +++++++++++++-----
 3 files changed, 126 insertions(+), 33 deletions(-)
 create mode 100644 api/net/sf/briar/api/crypto/SecretStorageKey.java

diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index 5cc912dc9c..7684c549b1 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -10,6 +10,12 @@ import javax.crypto.SecretKey;
 
 public interface CryptoComponent {
 
+	SecretKey deriveMacKey(byte[] secret, boolean alice);
+
+	SecretKey derivePacketKey(byte[] secret, boolean alice);
+
+	SecretKey deriveTagKey(byte[] secret, boolean alice);
+
 	KeyPair generateKeyPair();
 
 	SecretKey generateSecretKey();
diff --git a/api/net/sf/briar/api/crypto/SecretStorageKey.java b/api/net/sf/briar/api/crypto/SecretStorageKey.java
new file mode 100644
index 0000000000..12fbffa940
--- /dev/null
+++ b/api/net/sf/briar/api/crypto/SecretStorageKey.java
@@ -0,0 +1,18 @@
+package net.sf.briar.api.crypto;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import com.google.inject.BindingAnnotation;
+
+/**
+ * Annotation for injecting the key that is used for encrypting and decrypting
+ * secrets stored in the database.
+ */
+@BindingAnnotation
+@Target({ PARAMETER })
+@Retention(RUNTIME)
+public @interface SecretStorageKey {}
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 7e4d875250..c02a668a89 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -1,5 +1,8 @@
 package net.sf.briar.crypto;
 
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
@@ -7,50 +10,116 @@ import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.Security;
 import java.security.Signature;
+import java.util.Arrays;
 
+import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.KeyGenerator;
 import javax.crypto.Mac;
 import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
 
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.SecretStorageKey;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
+import com.google.inject.Inject;
+
 class CryptoComponentImpl implements CryptoComponent {
 
 	private static final String PROVIDER = "BC";
 	private static final String DIGEST_ALGO = "SHA-256";
 	private static final String KEY_PAIR_ALGO = "ECDSA";
-	private static final int KEY_PAIR_KEYSIZE = 256; // Bits
+	private static final int KEY_PAIR_BITS = 256;
+	private static final String SECRET_STORAGE_ALGO = "AES/CTR/NoPadding";
 	private static final String MAC_ALGO = "HMacSHA256";
 	private static final String PACKET_CIPHER_ALGO = "AES/CTR/NoPadding";
 	private static final String SECRET_KEY_ALGO = "AES";
-	private static final int SECRET_KEY_KEYSIZE = 256; // Bits
+	private static final int SECRET_KEY_BITS = 256;
 	private static final String SIGNATURE_ALGO = "ECDSA";
 	private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
 
+	private final SecretKey secretStorageKey;
 	private final KeyParser keyParser;
 	private final KeyPairGenerator keyPairGenerator;
 	private final KeyGenerator keyGenerator;
 
-	CryptoComponentImpl() {
+	@Inject
+	CryptoComponentImpl(@SecretStorageKey SecretKey secretStorageKey) {
 		Security.addProvider(new BouncyCastleProvider());
+		this.secretStorageKey = secretStorageKey;
 		try {
 			keyParser = new KeyParserImpl(KEY_PAIR_ALGO, PROVIDER);
 			keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGO,
 					PROVIDER);
-			keyPairGenerator.initialize(KEY_PAIR_KEYSIZE);
+			keyPairGenerator.initialize(KEY_PAIR_BITS);
 			keyGenerator = KeyGenerator.getInstance(SECRET_KEY_ALGO,
 					PROVIDER);
-			keyGenerator.init(SECRET_KEY_KEYSIZE);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+			keyGenerator.init(SECRET_KEY_BITS);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public SecretKey deriveMacKey(byte[] secret, boolean alice) {
+		if(alice) return deriveKey("MACA", secret);
+		else return deriveKey("MACB", secret);
+	}
+
+	private SecretKey deriveKey(String name, byte[] secret) {
+		MessageDigest digest = getMessageDigest();
+		try {
+			digest.update(name.getBytes("UTF-8"));
+		} catch(UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
 		}
+		byte[] decrypted = decryptSharedSecret(secret);
+		digest.update(decrypted);
+		Arrays.fill(decrypted, (byte) 0);
+		return new SecretKeySpec(digest.digest(), SECRET_KEY_ALGO);
+	}
+
+	private byte[] decryptSharedSecret(byte[] secret) {
+		// The first 16 bytes of the stored secret are the IV
+		if(secret.length <= 16) throw new IllegalArgumentException();
+		IvParameterSpec iv = new IvParameterSpec(secret, 0, 16);
+		try {
+			// Decrypt and return the remainder of the stored secret
+			Cipher c = Cipher.getInstance(SECRET_STORAGE_ALGO, PROVIDER);
+			c.init(Cipher.DECRYPT_MODE, secretStorageKey, iv);
+			return c.doFinal(secret, 16, secret.length - 16);
+		} catch(BadPaddingException e) {
+			throw new RuntimeException(e);
+		} catch(IllegalBlockSizeException e) {
+			throw new RuntimeException(e);
+		} catch(InvalidAlgorithmParameterException e) {
+			throw new RuntimeException(e);
+		} catch(InvalidKeyException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchPaddingException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public SecretKey derivePacketKey(byte[] secret, boolean alice) {
+		if(alice) return deriveKey("PKTA", secret);
+		else return deriveKey("PKTB", secret);
+	}
+
+	public SecretKey deriveTagKey(byte[] secret, boolean alice) {
+		if(alice) return deriveKey("TAGA", secret);
+		else return deriveKey("TAGB", secret);
 	}
 
 	public KeyPair generateKeyPair() {
@@ -68,54 +137,54 @@ class CryptoComponentImpl implements CryptoComponent {
 	public Mac getMac() {
 		try {
 			return Mac.getInstance(MAC_ALGO, PROVIDER);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
 		}
 	}
 
 	public MessageDigest getMessageDigest() {
 		try {
 			return MessageDigest.getInstance(DIGEST_ALGO, PROVIDER);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
 		}
 	}
 
 	public Cipher getPacketCipher() {
 		try {
 			return Cipher.getInstance(PACKET_CIPHER_ALGO, PROVIDER);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchPaddingException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchPaddingException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
 		}
 	}
 
 	public Signature getSignature() {
 		try {
 			return Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
 		}
 	}
 
 	public Cipher getTagCipher() {
 		try {
 			return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
-		} catch(NoSuchAlgorithmException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchPaddingException impossible) {
-			throw new RuntimeException(impossible);
-		} catch(NoSuchProviderException impossible) {
-			throw new RuntimeException(impossible);
+		} catch(NoSuchAlgorithmException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchPaddingException e) {
+			throw new RuntimeException(e);
+		} catch(NoSuchProviderException e) {
+			throw new RuntimeException(e);
 		}
 	}
 }
-- 
GitLab