From 7fbad8dc26cfe27535ba2d7d5ac0360abd374ff6 Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Wed, 14 Jan 2015 20:46:03 +0000 Subject: [PATCH] Use FortunaGenerator to implement PseudoRandom. --- .../briarproject/android/AndroidModule.java | 7 +- .../android/PasswordActivity.java | 3 +- .../briarproject/android/SetupActivity.java | 7 +- .../briarproject/api/crypto/SecretKey.java | 3 + .../briarproject/api/db/DatabaseConfig.java | 6 +- .../crypto/CryptoComponentImpl.java | 82 +++++++------------ .../briarproject/crypto/PseudoRandomImpl.java | 38 +++------ .../src/org/briarproject/db/H2Database.java | 2 +- .../org/briarproject/TestDatabaseConfig.java | 7 +- 9 files changed, 63 insertions(+), 92 deletions(-) diff --git a/briar-android/src/org/briarproject/android/AndroidModule.java b/briar-android/src/org/briarproject/android/AndroidModule.java index d3b2029e70..1adf330941 100644 --- a/briar-android/src/org/briarproject/android/AndroidModule.java +++ b/briar-android/src/org/briarproject/android/AndroidModule.java @@ -9,6 +9,7 @@ import javax.inject.Singleton; import org.briarproject.api.android.AndroidExecutor; import org.briarproject.api.android.AndroidNotificationManager; import org.briarproject.api.android.ReferenceManager; +import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.ui.UiCallback; @@ -54,7 +55,7 @@ public class AndroidModule extends AbstractModule { final File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE); return new DatabaseConfig() { - private volatile byte[] key = null; + private volatile SecretKey key = null; public boolean databaseExists() { return dir.isDirectory() && dir.listFiles().length > 0; @@ -64,11 +65,11 @@ public class AndroidModule extends AbstractModule { return dir; } - public void setEncryptionKey(byte[] key) { + public void setEncryptionKey(SecretKey key) { this.key = key; } - public byte[] getEncryptionKey() { + public SecretKey getEncryptionKey() { return key; } diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java index 727985f1b1..ce3672019c 100644 --- a/briar-android/src/org/briarproject/android/PasswordActivity.java +++ b/briar-android/src/org/briarproject/android/PasswordActivity.java @@ -23,6 +23,7 @@ import org.briarproject.android.util.FixedVerticalSpace; import org.briarproject.android.util.LayoutUtils; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoExecutor; +import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.util.StringUtils; @@ -140,7 +141,7 @@ public class PasswordActivity extends RoboActivity { if(key == null) { tryAgain(); } else { - databaseConfig.setEncryptionKey(key); + databaseConfig.setEncryptionKey(new SecretKey(key)); setResultAndFinish(); } } diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index 109ce0cee8..d6e6d7a5bd 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -34,6 +34,7 @@ import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.crypto.KeyPair; import org.briarproject.api.crypto.PasswordStrengthEstimator; +import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.util.StringUtils; @@ -225,7 +226,7 @@ OnEditorActionListener { // Store the DB key and create the identity in a background thread cryptoExecutor.execute(new Runnable() { public void run() { - byte[] key = crypto.generateSecretKey().getBytes(); + SecretKey key = crypto.generateSecretKey(); databaseConfig.setEncryptionKey(key); byte[] encrypted = encryptDatabaseKey(key, password); storeEncryptedDatabaseKey(encrypted); @@ -247,9 +248,9 @@ OnEditorActionListener { LOG.info("Key storage took " + duration + " ms"); } - private byte[] encryptDatabaseKey(byte[] key, String password) { + private byte[] encryptDatabaseKey(SecretKey key, String password) { long now = System.currentTimeMillis(); - byte[] encrypted = crypto.encryptWithPassword(key, password); + byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password); long duration = System.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) LOG.info("Key derivation took " + duration + " ms"); diff --git a/briar-api/src/org/briarproject/api/crypto/SecretKey.java b/briar-api/src/org/briarproject/api/crypto/SecretKey.java index 62b75a9452..f863bbebee 100644 --- a/briar-api/src/org/briarproject/api/crypto/SecretKey.java +++ b/briar-api/src/org/briarproject/api/crypto/SecretKey.java @@ -3,9 +3,12 @@ package org.briarproject.api.crypto; /** A secret key used for encryption and/or authentication. */ public class SecretKey { + public static final int LENGTH = 32; // Bytes + private final byte[] key; public SecretKey(byte[] key) { + if(key.length != LENGTH) throw new IllegalArgumentException(); this.key = key; } diff --git a/briar-api/src/org/briarproject/api/db/DatabaseConfig.java b/briar-api/src/org/briarproject/api/db/DatabaseConfig.java index e452fe15c5..862afe1c8d 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseConfig.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseConfig.java @@ -2,15 +2,17 @@ package org.briarproject.api.db; import java.io.File; +import org.briarproject.api.crypto.SecretKey; + public interface DatabaseConfig { boolean databaseExists(); File getDatabaseDirectory(); - void setEncryptionKey(byte[] key); + void setEncryptionKey(SecretKey key); - byte[] getEncryptionKey(); + SecretKey getEncryptionKey(); long getMaxSize(); } diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index be63cd6178..989a9b28a6 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -9,7 +9,6 @@ import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED; import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.logging.Logger; @@ -49,7 +48,6 @@ class CryptoComponentImpl implements CryptoComponent { private static final Logger LOG = Logger.getLogger(CryptoComponentImpl.class.getName()); - private static final int CIPHER_KEY_BYTES = 32; // 256 bits private static final int AGREEMENT_KEY_PAIR_BITS = 256; private static final int SIGNATURE_KEY_PAIR_BITS = 256; private static final int STORAGE_IV_BYTES = 16; // 128 bits @@ -73,8 +71,6 @@ class CryptoComponentImpl implements CryptoComponent { { 'A', '_', 'F', 'R', 'A', 'M', 'E', '\0' }; private static final byte[] B_FRAME = { 'B', '_', 'F', 'R', 'A', 'M', 'E', '\0' }; - // Blank secret for argument validation - private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES]; private final SecureRandom secureRandom; private final ECKeyPairGenerator agreementKeyPairGenerator; @@ -105,7 +101,7 @@ class CryptoComponentImpl implements CryptoComponent { } public SecretKey generateSecretKey() { - byte[] b = new byte[CIPHER_KEY_BYTES]; + byte[] b = new byte[SecretKey.LENGTH]; secureRandom.nextBytes(b); return new SecretKey(b); } @@ -115,7 +111,7 @@ class CryptoComponentImpl implements CryptoComponent { } public PseudoRandom getPseudoRandom(int seed1, int seed2) { - return new PseudoRandomImpl(getMessageDigest(), seed1, seed2); + return new PseudoRandomImpl(seed1, seed2); } public SecureRandom getSecureRandom() { @@ -172,9 +168,7 @@ class CryptoComponentImpl implements CryptoComponent { } public int[] deriveConfirmationCodes(byte[] secret) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); byte[] alice = counterModeKdf(secret, CODE, 0); byte[] bob = counterModeKdf(secret, CODE, 1); @@ -185,9 +179,7 @@ class CryptoComponentImpl implements CryptoComponent { } public byte[][] deriveInvitationNonces(byte[] secret) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); byte[] alice = counterModeKdf(secret, NONCE, 0); byte[] bob = counterModeKdf(secret, NONCE, 1); @@ -237,26 +229,20 @@ class CryptoComponentImpl implements CryptoComponent { } public byte[] deriveGroupSalt(byte[] secret) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); return counterModeKdf(secret, SALT, 0); } public byte[] deriveInitialSecret(byte[] secret, int transportIndex) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); if(transportIndex < 0) throw new IllegalArgumentException(); return counterModeKdf(secret, FIRST, transportIndex); } public byte[] deriveNextSecret(byte[] secret, long period) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); if(period < 0 || period > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); @@ -264,9 +250,7 @@ class CryptoComponentImpl implements CryptoComponent { } public SecretKey deriveTagKey(byte[] secret, boolean alice) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); if(alice) return deriveKey(secret, A_TAG, 0); else return deriveKey(secret, B_TAG, 0); @@ -274,9 +258,7 @@ class CryptoComponentImpl implements CryptoComponent { public SecretKey deriveFrameKey(byte[] secret, long streamNumber, boolean alice) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); if(streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) throw new IllegalArgumentException(); @@ -366,32 +348,30 @@ class CryptoComponentImpl implements CryptoComponent { // Key derivation function based on a hash function - see NIST SP 800-56A, // section 5.8 - private byte[] concatenationKdf(byte[]... args) { + private byte[] concatenationKdf(byte[]... inputs) { // The output of the hash function must be long enough to use as a key MessageDigest messageDigest = getMessageDigest(); - if(messageDigest.getDigestLength() < CIPHER_KEY_BYTES) + if(messageDigest.getDigestLength() < SecretKey.LENGTH) throw new RuntimeException(); - // Each argument is length-prefixed - the length must fit in an + // Each input is length-prefixed - the length must fit in an // unsigned 8-bit integer - for(byte[] arg : args) { - if(arg.length > 255) throw new IllegalArgumentException(); - messageDigest.update((byte) arg.length); - messageDigest.update(arg); + for(byte[] input : inputs) { + if(input.length > 255) throw new IllegalArgumentException(); + messageDigest.update((byte) input.length); + messageDigest.update(input); } byte[] hash = messageDigest.digest(); - // The output is the first CIPHER_KEY_BYTES bytes of the hash - if(hash.length == CIPHER_KEY_BYTES) return hash; - byte[] output = new byte[CIPHER_KEY_BYTES]; - System.arraycopy(hash, 0, output, 0, output.length); - return output; + // The output is the first SecretKey.LENGTH bytes of the hash + if(hash.length == SecretKey.LENGTH) return hash; + byte[] truncated = new byte[SecretKey.LENGTH]; + System.arraycopy(hash, 0, truncated, 0, truncated.length); + return truncated; } // Key derivation function based on a PRF in counter mode - see // NIST SP 800-108, section 5.1 private byte[] counterModeKdf(byte[] secret, byte[] label, long context) { - if(secret.length != CIPHER_KEY_BYTES) - throw new IllegalArgumentException(); - if(Arrays.equals(secret, BLANK_SECRET)) + if(secret.length != SecretKey.LENGTH) throw new IllegalArgumentException(); // The label must be null-terminated if(label[label.length - 1] != '\0') @@ -402,20 +382,20 @@ class CryptoComponentImpl implements CryptoComponent { prf.init(k); int macLength = prf.getMacSize(); // The output of the PRF must be long enough to use as a key - if(macLength < CIPHER_KEY_BYTES) throw new RuntimeException(); + if(macLength < SecretKey.LENGTH) throw new RuntimeException(); byte[] mac = new byte[macLength]; prf.update((byte) 0); // Counter prf.update(label, 0, label.length); // Null-terminated byte[] contextBytes = new byte[4]; ByteUtils.writeUint32(context, contextBytes, 0); prf.update(contextBytes, 0, contextBytes.length); - prf.update((byte) CIPHER_KEY_BYTES); // Output length + prf.update((byte) SecretKey.LENGTH); // Output length prf.doFinal(mac, 0); - // The output is the first CIPHER_KEY_BYTES bytes of the MAC - if(mac.length == CIPHER_KEY_BYTES) return mac; - byte[] output = new byte[CIPHER_KEY_BYTES]; - System.arraycopy(mac, 0, output, 0, output.length); - return output; + // The output is the first SecretKey.LENGTH bytes of the MAC + if(mac.length == SecretKey.LENGTH) return mac; + byte[] truncated = new byte[SecretKey.LENGTH]; + System.arraycopy(mac, 0, truncated, 0, truncated.length); + return truncated; } // Password-based key derivation function - see PKCS#5 v2.1, section 5.2 @@ -424,7 +404,7 @@ class CryptoComponentImpl implements CryptoComponent { Digest digest = new SHA256Digest(); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest); gen.init(utf8, salt, iterations); - int keyLengthInBits = CIPHER_KEY_BYTES * 8; + int keyLengthInBits = SecretKey.LENGTH * 8; CipherParameters p = gen.generateDerivedParameters(keyLengthInBits); return ((KeyParameter) p).getKey(); } @@ -461,7 +441,7 @@ class CryptoComponentImpl implements CryptoComponent { private long sampleRunningTime(int iterations) { byte[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' }; byte[] salt = new byte[PBKDF_SALT_BYTES]; - int keyLengthInBits = CIPHER_KEY_BYTES * 8; + int keyLengthInBits = SecretKey.LENGTH * 8; long start = System.nanoTime(); Digest digest = new SHA256Digest(); PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest); diff --git a/briar-core/src/org/briarproject/crypto/PseudoRandomImpl.java b/briar-core/src/org/briarproject/crypto/PseudoRandomImpl.java index de83754f96..b29dc806d2 100644 --- a/briar-core/src/org/briarproject/crypto/PseudoRandomImpl.java +++ b/briar-core/src/org/briarproject/crypto/PseudoRandomImpl.java @@ -1,41 +1,23 @@ package org.briarproject.crypto; -import org.briarproject.api.crypto.MessageDigest; import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.util.ByteUtils; class PseudoRandomImpl implements PseudoRandom { - private final MessageDigest messageDigest; + private final FortunaGenerator generator; - private byte[] state; - private int offset; - - PseudoRandomImpl(MessageDigest messageDigest, int seed1, int seed2) { - this.messageDigest = messageDigest; - byte[] seedBytes = new byte[8]; - ByteUtils.writeUint32(seed1, seedBytes, 0); - ByteUtils.writeUint32(seed2, seedBytes, 4); - messageDigest.update(seedBytes); - state = messageDigest.digest(); - offset = 0; + PseudoRandomImpl(int seed1, int seed2) { + byte[] seed = new byte[8]; + ByteUtils.writeUint32(seed1, seed, 0); + ByteUtils.writeUint32(seed2, seed, 4); + generator = new FortunaGenerator(seed); } - public synchronized byte[] nextBytes(int bytes) { - byte[] b = new byte[bytes]; - int half = state.length / 2; - int off = 0, len = b.length, available = half - offset; - while(available < len) { - System.arraycopy(state, offset, b, off, available); - off += available; - len -= available; - messageDigest.update(state, half, half); - state = messageDigest.digest(); - offset = 0; - available = half; - } - System.arraycopy(state, offset, b, off, len); - offset += len; + public synchronized byte[] nextBytes(int length) { + byte[] b = new byte[length]; + int offset = 0; + while(offset < length) offset += generator.nextBytes(b, offset, length); return b; } } diff --git a/briar-core/src/org/briarproject/db/H2Database.java b/briar-core/src/org/briarproject/db/H2Database.java index fc91e20dca..f357593910 100644 --- a/briar-core/src/org/briarproject/db/H2Database.java +++ b/briar-core/src/org/briarproject/db/H2Database.java @@ -83,7 +83,7 @@ class H2Database extends JdbcDatabase { @Override protected Connection createConnection() throws SQLException { - byte[] key = config.getEncryptionKey(); + byte[] key = config.getEncryptionKey().getBytes(); if(key == null) throw new IllegalStateException(); Properties props = new Properties(); props.setProperty("user", "user"); diff --git a/briar-tests/src/org/briarproject/TestDatabaseConfig.java b/briar-tests/src/org/briarproject/TestDatabaseConfig.java index 27eb702845..285a6c42a5 100644 --- a/briar-tests/src/org/briarproject/TestDatabaseConfig.java +++ b/briar-tests/src/org/briarproject/TestDatabaseConfig.java @@ -2,13 +2,14 @@ package org.briarproject; import java.io.File; +import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; public class TestDatabaseConfig implements DatabaseConfig { private final File dir; private final long maxSize; - private volatile byte[] key = new byte[] { 'f', 'o', 'o' }; + private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]); public TestDatabaseConfig(File dir, long maxSize) { this.dir = dir; @@ -23,11 +24,11 @@ public class TestDatabaseConfig implements DatabaseConfig { return dir; } - public void setEncryptionKey(byte[] key) { + public void setEncryptionKey(SecretKey key) { this.key = key; } - public byte[] getEncryptionKey() { + public SecretKey getEncryptionKey() { return key; } -- GitLab