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