From 9215537d8042b3525d5c0fe2aaf5d07d1816bdf5 Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Tue, 19 Feb 2013 00:41:50 +0000 Subject: [PATCH] Refactored bundle encryption code. --- .../sf/briar/android/BundleEncrypterImpl.java | 52 ++++-------------- .../sf/briar/api/crypto/CryptoComponent.java | 15 +++++- .../sf/briar/crypto/CryptoComponentImpl.java | 53 ++++++++++++++++++- 3 files changed, 75 insertions(+), 45 deletions(-) diff --git a/briar-android/src/net/sf/briar/android/BundleEncrypterImpl.java b/briar-android/src/net/sf/briar/android/BundleEncrypterImpl.java index 68305ffe6c..108c3d78a9 100644 --- a/briar-android/src/net/sf/briar/android/BundleEncrypterImpl.java +++ b/briar-android/src/net/sf/briar/android/BundleEncrypterImpl.java @@ -1,41 +1,27 @@ package net.sf.briar.android; import static java.util.logging.Level.INFO; -import static javax.crypto.Cipher.DECRYPT_MODE; -import static javax.crypto.Cipher.ENCRYPT_MODE; -import java.security.GeneralSecurityException; -import java.security.SecureRandom; import java.util.logging.Logger; import net.sf.briar.api.android.BundleEncrypter; -import net.sf.briar.api.crypto.AuthenticatedCipher; import net.sf.briar.api.crypto.CryptoComponent; -import net.sf.briar.api.crypto.ErasableKey; import net.sf.briar.util.ByteUtils; import android.os.Bundle; import android.os.Parcel; import com.google.inject.Inject; -// This class is not thread-safe class BundleEncrypterImpl implements BundleEncrypter { private static final Logger LOG = Logger.getLogger(BundleEncrypterImpl.class.getName()); - private final AuthenticatedCipher cipher; - private final SecureRandom random; - private final ErasableKey key; - private final int blockSize, macLength; + private final CryptoComponent crypto; @Inject BundleEncrypterImpl(CryptoComponent crypto) { - cipher = crypto.getBundleCipher(); - random = crypto.getSecureRandom(); - key = crypto.generateSecretKey(); - blockSize = cipher.getBlockSize(); - macLength = cipher.getMacLength(); + this.crypto = crypto; } @Override @@ -49,41 +35,23 @@ class BundleEncrypterImpl implements BundleEncrypter { LOG.info("Marshalled " + b.size() + " mappings, " + plaintext.length + " plaintext bytes"); } - // Encrypt the byte array using a random IV - byte[] iv = new byte[blockSize]; - random.nextBytes(iv); - byte[] ciphertext = new byte[plaintext.length + macLength]; - try { - cipher.init(ENCRYPT_MODE, key, iv, null); - cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); - } catch(GeneralSecurityException e) { - throw new RuntimeException(e); - } + // Encrypt the plaintext + byte[] ciphertext = crypto.encryptTemporaryStorage(plaintext); ByteUtils.erase(plaintext); - // Replace the plaintext contents with the IV and the ciphertext + // Replace the plaintext contents with the ciphertext b.clear(); - b.putByteArray("net.sf.briar.IV", iv); b.putByteArray("net.sf.briar.CIPHERTEXT", ciphertext); } @Override public boolean decrypt(Bundle b) { - // Retrieve the IV and the ciphertext - byte[] iv = b.getByteArray("net.sf.briar.IV"); - if(iv == null) throw new IllegalArgumentException(); - if(iv.length != blockSize) throw new IllegalArgumentException(); + // Retrieve the ciphertext byte[] ciphertext = b.getByteArray("net.sf.briar.CIPHERTEXT"); if(ciphertext == null) throw new IllegalArgumentException(); - if(ciphertext.length < macLength) throw new IllegalArgumentException(); - // Decrypt the ciphertext using the IV - byte[] plaintext = new byte[ciphertext.length - macLength]; - try { - cipher.init(DECRYPT_MODE, key, iv, null); - cipher.doFinal(ciphertext, 0, ciphertext.length, plaintext, 0); - } catch(GeneralSecurityException e) { - return false; // Invalid ciphertext - } - // Unmarshall the byte array + // Decrypt the ciphertext + byte[] plaintext = crypto.decryptTemporaryStorage(ciphertext); + if(plaintext == null) return false; + // Unmarshall the plaintext Parcel p = Parcel.obtain(); p.unmarshall(plaintext, 0, plaintext.length); ByteUtils.erase(plaintext); diff --git a/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java b/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java index e59df9c5ef..aa88207c16 100644 --- a/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java +++ b/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java @@ -77,7 +77,18 @@ public interface CryptoComponent { AuthenticatedCipher getFrameCipher(); - AuthenticatedCipher getBundleCipher(); - Signature getSignature(); + + /** + * Encrypts the given plaintext so it can be written to temporary storage. + * The ciphertext will not be decryptable after the app restarts. + */ + byte[] encryptTemporaryStorage(byte[] plaintext); + + /** + * Decrypts the given ciphertext that has been read from temporary storage. + * Returns null if the ciphertext is not decryptable (for example, if it + * was written before the app restarted). + */ + byte[] decryptTemporaryStorage(byte[] ciphertext); } diff --git a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java index 344a863bc3..0ee33c10cb 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 javax.crypto.Cipher.DECRYPT_MODE; import static javax.crypto.Cipher.ENCRYPT_MODE; import static net.sf.briar.api.plugins.InvitationConstants.CODE_BITS; import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; @@ -7,6 +8,7 @@ import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import java.math.BigInteger; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -56,6 +58,7 @@ class CryptoComponentImpl implements CryptoComponent { private static final String SIGNATURE_ALGO = "ECDSA"; private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding"; private static final int GCM_MAC_LENGTH = 16; // 128 bits + private static final int STORAGE_IV_LENGTH = 32; // 256 bits // Labels for key derivation private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' }; @@ -116,6 +119,7 @@ class CryptoComponentImpl implements CryptoComponent { private final KeyPairGenerator agreementKeyPairGenerator; private final KeyPairGenerator signatureKeyPairGenerator; private final SecureRandom secureRandom; + private final ErasableKey temporaryStorageKey; @Inject CryptoComponentImpl() { @@ -139,6 +143,7 @@ class CryptoComponentImpl implements CryptoComponent { throw new RuntimeException(e); } secureRandom = new SecureRandom(); + temporaryStorageKey = generateSecretKey(); } public ErasableKey deriveTagKey(byte[] secret, boolean alice) { @@ -378,10 +383,56 @@ class CryptoComponentImpl implements CryptoComponent { return new AuthenticatedCipherImpl(cipher, GCM_MAC_LENGTH); } - public AuthenticatedCipher getBundleCipher() { + private AuthenticatedCipher getTemporaryStorageCipher() { // This code is specific to BouncyCastle because javax.crypto.Cipher // doesn't support additional authenticated data until Java 7 AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); return new AuthenticatedCipherImpl(cipher, GCM_MAC_LENGTH); } + + public byte[] encryptTemporaryStorage(byte[] plaintext) { + AuthenticatedCipher cipher = getTemporaryStorageCipher(); + // Generate a random IV + byte[] iv = new byte[STORAGE_IV_LENGTH]; + secureRandom.nextBytes(iv); + // The output contains the IV, ciphertext and MAC + int ciphertextLength = iv.length + plaintext.length + GCM_MAC_LENGTH; + byte[] ciphertext = new byte[ciphertextLength]; + System.arraycopy(iv, 0, ciphertext, 0, iv.length); + // Initialise the cipher and encrypt the plaintext + try { + cipher.init(ENCRYPT_MODE, temporaryStorageKey, iv, null); + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, + iv.length); + } catch(GeneralSecurityException e) { + throw new RuntimeException(e); + } + return ciphertext; + } + + public byte[] decryptTemporaryStorage(byte[] ciphertext) { + // The input contains the IV, ciphertext and MAC + if(ciphertext.length < STORAGE_IV_LENGTH + GCM_MAC_LENGTH) + throw new IllegalArgumentException(); + AuthenticatedCipher cipher = getTemporaryStorageCipher(); + // Copy the IV + byte[] iv = new byte[STORAGE_IV_LENGTH]; + System.arraycopy(ciphertext, 0, iv, 0, iv.length); + // Initialise the cipher + try { + cipher.init(DECRYPT_MODE, temporaryStorageKey, iv, null); + } catch(InvalidKeyException e) { + throw new RuntimeException(e); + } + // Try to decrypt the ciphertext (may be invalid) + int plaintextLength = ciphertext.length - iv.length - GCM_MAC_LENGTH; + byte[] plaintext = new byte[plaintextLength]; + try { + cipher.doFinal(ciphertext, iv.length, ciphertext.length - iv.length, + plaintext, 0); + } catch(GeneralSecurityException e) { + return null; // Invalid ciphertext + } + return plaintext; + } } -- GitLab