diff --git a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java index d16009c13d4b288d3e4f1e3f6fd9bc0b8574b11d..670820c4ba4c0294fda0a8a8e06c970e4f6ecc47 100644 --- a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java +++ b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java @@ -1,5 +1,6 @@ package net.sf.briar.crypto; +import static java.util.logging.Level.INFO; import static javax.crypto.Cipher.DECRYPT_MODE; import static javax.crypto.Cipher.ENCRYPT_MODE; import static net.sf.briar.api.invitation.InvitationConstants.CODE_BITS; @@ -26,8 +27,10 @@ import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.EllipticCurve; import java.util.Arrays; +import java.util.logging.Logger; import javax.crypto.Cipher; +import javax.crypto.CipherSpi; import javax.crypto.KeyAgreement; import javax.crypto.spec.IvParameterSpec; @@ -40,20 +43,25 @@ import net.sf.briar.api.crypto.PseudoRandom; import net.sf.briar.util.ByteUtils; import org.spongycastle.crypto.CipherParameters; -import org.spongycastle.crypto.engines.AESEngine; +import org.spongycastle.crypto.engines.AESFastEngine; import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.spongycastle.crypto.modes.AEADBlockCipher; import org.spongycastle.crypto.modes.GCMBlockCipher; import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi; +import org.spongycastle.jcajce.provider.digest.SHA384; +import org.spongycastle.jcajce.provider.symmetric.AES; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.util.Strings; class CryptoComponentImpl implements CryptoComponent { + private static final Logger LOG = + Logger.getLogger(CryptoComponentImpl.class.getName()); + private static final String PROVIDER = "SC"; // Spongy Castle private static final String SECRET_KEY_ALGO = "AES"; private static final int SECRET_KEY_BYTES = 32; // 256 bits - private static final String DIGEST_ALGO = "SHA-384"; private static final String AGREEMENT_ALGO = "ECDHC"; private static final String AGREEMENT_KEY_PAIR_ALGO = "ECDH"; private static final int AGREEMENT_KEY_PAIR_BITS = 384; @@ -62,7 +70,6 @@ class CryptoComponentImpl implements CryptoComponent { private static final int SIGNATURE_KEY_PAIR_BITS = 384; private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding"; private static final int GCM_MAC_BYTES = 16; // 128 bits - private static final String STORAGE_CIPHER_ALGO = "AES/GCM/NoPadding"; private static final int STORAGE_IV_BYTES = 16; // 128 bits private static final int PBKDF_SALT_BYTES = 16; // 128 bits private static final int PBKDF_ITERATIONS = 1000; @@ -145,12 +152,20 @@ class CryptoComponentImpl implements CryptoComponent { 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 = new KeyPairGeneratorSpi.ECDH(); agreementKeyPairGenerator.initialize(AGREEMENT_KEY_PAIR_BITS); - signatureKeyPairGenerator = KeyPairGenerator.getInstance( - SIGNATURE_KEY_PAIR_ALGO, PROVIDER); + signatureKeyPairGenerator = new KeyPairGeneratorSpi.ECDSA(); signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS); + if(LOG.isLoggable(INFO)) { + LOG.info("Agreement KeyFactory: " + + agreementKeyFactory.getClass().getName()); + LOG.info("Signature KeyFactory: " + + signatureKeyFactory.getClass().getName()); + LOG.info("Agreement KeyPairGenerator: " + + agreementKeyPairGenerator.getClass().getName()); + LOG.info("Signature KeyPairGenerator: " + + signatureKeyPairGenerator.getClass().getName()); + } } catch(GeneralSecurityException e) { throw new RuntimeException(e); } @@ -164,12 +179,7 @@ class CryptoComponentImpl implements CryptoComponent { } public MessageDigest getMessageDigest() { - try { - return new DoubleDigest(java.security.MessageDigest.getInstance( - DIGEST_ALGO, PROVIDER)); - } catch(GeneralSecurityException e) { - throw new RuntimeException(e); - } + return new DoubleDigest(new SHA384.Digest()); } public PseudoRandom getPseudoRandom(int seed1, int seed2) { @@ -182,7 +192,11 @@ class CryptoComponentImpl implements CryptoComponent { public Signature getSignature() { try { - return Signature.getInstance(SIGNATURE_ALGO, PROVIDER); + Signature signature = Signature.getInstance(SIGNATURE_ALGO, + PROVIDER); + if(LOG.isLoggable(INFO)) + LOG.info("Signature: " + signature.getClass().getName()); + return signature; } catch(GeneralSecurityException e) { throw new RuntimeException(e); } @@ -280,6 +294,8 @@ class CryptoComponentImpl implements CryptoComponent { throws GeneralSecurityException { KeyAgreement keyAgreement = KeyAgreement.getInstance(AGREEMENT_ALGO, PROVIDER); + if(LOG.isLoggable(INFO)) + LOG.info("KeyAgreement: " + keyAgreement.getClass().getName()); keyAgreement.init(priv); keyAgreement.doPhase(pub, true); return keyAgreement.generateSecret(); @@ -340,17 +356,11 @@ class CryptoComponentImpl implements CryptoComponent { } public Cipher getTagCipher() { - try { - return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER); - } catch(GeneralSecurityException e) { - throw new RuntimeException(e); - } + return new CipherFromSpi(new AES.ECB(), TAG_CIPHER_ALGO); } public AuthenticatedCipher getFrameCipher() { - // This code is specific to Spongy Castle because javax.crypto.Cipher - // doesn't support additional authenticated data until Java 7 - AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); + AEADBlockCipher cipher = new GCMBlockCipher(new AESFastEngine()); return new AuthenticatedCipherImpl(cipher, GCM_MAC_BYTES); } @@ -378,23 +388,21 @@ class CryptoComponentImpl implements CryptoComponent { byte[] keyBytes = pbkdf2(password, salt); ErasableKey key = new ErasableKeyImpl(keyBytes, SECRET_KEY_ALGO); // Generate a random IV - byte[] ivBytes = new byte[STORAGE_IV_BYTES]; - secureRandom.nextBytes(ivBytes); - IvParameterSpec iv = new IvParameterSpec(ivBytes); + byte[] iv = new byte[STORAGE_IV_BYTES]; + secureRandom.nextBytes(iv); // The output contains the salt, IV, ciphertext and MAC - int outputLen = PBKDF_SALT_BYTES + STORAGE_IV_BYTES + input.length - + GCM_MAC_BYTES; + int outputLen = salt.length + iv.length + input.length + GCM_MAC_BYTES; byte[] output = new byte[outputLen]; - System.arraycopy(salt, 0, output, 0, PBKDF_SALT_BYTES); - System.arraycopy(ivBytes, 0, output, PBKDF_SALT_BYTES, - STORAGE_IV_BYTES); + System.arraycopy(salt, 0, output, 0, salt.length); + System.arraycopy(iv, 0, output, salt.length, iv.length); // Initialise the cipher and encrypt the plaintext - Cipher cipher; try { - cipher = Cipher.getInstance(STORAGE_CIPHER_ALGO, PROVIDER); - cipher.init(ENCRYPT_MODE, key, iv); - cipher.doFinal(input, 0, input.length, output, - PBKDF_SALT_BYTES + STORAGE_IV_BYTES); + AEADBlockCipher c = new GCMBlockCipher(new AESFastEngine()); + AuthenticatedCipher cipher = new AuthenticatedCipherImpl(c, + GCM_MAC_BYTES); + cipher.init(ENCRYPT_MODE, key, iv, null); + int outputOff = salt.length + iv.length; + cipher.doFinal(input, 0, input.length, output, outputOff); return output; } catch(GeneralSecurityException e) { throw new RuntimeException(e); @@ -408,25 +416,29 @@ class CryptoComponentImpl implements CryptoComponent { if(input.length < PBKDF_SALT_BYTES + STORAGE_IV_BYTES + GCM_MAC_BYTES) return null; // Invalid byte[] salt = new byte[PBKDF_SALT_BYTES]; - System.arraycopy(input, 0, salt, 0, PBKDF_SALT_BYTES); - IvParameterSpec iv = new IvParameterSpec(input, PBKDF_SALT_BYTES, - STORAGE_IV_BYTES); + System.arraycopy(input, 0, salt, 0, salt.length); + byte[] iv = new byte[STORAGE_IV_BYTES]; + System.arraycopy(input, salt.length, iv, 0, iv.length); // Derive the key from the password byte[] keyBytes = pbkdf2(password, salt); ErasableKey key = new ErasableKeyImpl(keyBytes, SECRET_KEY_ALGO); // Initialise the cipher - Cipher cipher; + AuthenticatedCipher cipher; try { - cipher = Cipher.getInstance(STORAGE_CIPHER_ALGO, PROVIDER); - cipher.init(DECRYPT_MODE, key, iv); + AEADBlockCipher c = new GCMBlockCipher(new AESFastEngine()); + cipher = new AuthenticatedCipherImpl(c, GCM_MAC_BYTES); + cipher.init(DECRYPT_MODE, key, iv, null); } catch(GeneralSecurityException e) { key.erase(); throw new RuntimeException(e); } // Try to decrypt the ciphertext (may be invalid) try { - return cipher.doFinal(input, PBKDF_SALT_BYTES + STORAGE_IV_BYTES, - input.length - PBKDF_SALT_BYTES - STORAGE_IV_BYTES); + int inputOff = salt.length + iv.length; + int inputLen = input.length - salt.length - iv.length; + byte[] output = new byte[inputLen - GCM_MAC_BYTES]; + cipher.doFinal(input, inputOff, inputLen, output, 0); + return output; } catch(GeneralSecurityException e) { return null; // Invalid } finally { @@ -476,7 +488,7 @@ class CryptoComponentImpl implements CryptoComponent { byte[] hash = messageDigest.digest(); // The secret is the first SECRET_KEY_BYTES bytes of the hash byte[] output = new byte[SECRET_KEY_BYTES]; - System.arraycopy(hash, 0, output, 0, SECRET_KEY_BYTES); + System.arraycopy(hash, 0, output, 0, output.length); ByteUtils.erase(hash); return output; } @@ -498,7 +510,8 @@ class CryptoComponentImpl implements CryptoComponent { IvParameterSpec iv = new IvParameterSpec(ivBytes); ErasableKey key = new ErasableKeyImpl(secret, SECRET_KEY_ALGO); try { - Cipher cipher = Cipher.getInstance(KEY_DERIVATION_ALGO, PROVIDER); + CipherSpi spi = new AES.ECB(); + Cipher cipher = new CipherFromSpi(spi, KEY_DERIVATION_ALGO); cipher.init(Cipher.ENCRYPT_MODE, key, iv); byte[] output = cipher.doFinal(KEY_DERIVATION_BLANK_PLAINTEXT); assert output.length == SECRET_KEY_BYTES; @@ -534,4 +547,11 @@ class CryptoComponentImpl implements CryptoComponent { throw new RuntimeException(e); } } + + private static class CipherFromSpi extends Cipher { + + private CipherFromSpi(CipherSpi spi, String transformation) { + super(spi, null, transformation); + } + } }