mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Use FortunaGenerator to implement PseudoRandom.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user