Use FortunaGenerator to implement PseudoRandom.

This commit is contained in:
akwizgran
2015-01-14 20:46:03 +00:00
parent 1c7432cac4
commit 7fbad8dc26
9 changed files with 63 additions and 92 deletions

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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");

View File

@@ -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;
}