mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
Refactor Android-specific code out of bramble-core.
This commit is contained in:
@@ -2,7 +2,7 @@ package org.briarproject.bramble.account;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
@@ -178,7 +178,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
|
||||
byte[] plaintext = key.getBytes();
|
||||
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password,
|
||||
databaseConfig.getKeyStoreConfig());
|
||||
databaseConfig.getKeyStrengthener());
|
||||
return storeEncryptedDatabaseKey(toHexString(ciphertext));
|
||||
}
|
||||
|
||||
@@ -211,19 +211,19 @@ class AccountManagerImpl implements AccountManager {
|
||||
return null;
|
||||
}
|
||||
byte[] ciphertext = fromHexString(hex);
|
||||
KeyStoreConfig keyStoreConfig = databaseConfig.getKeyStoreConfig();
|
||||
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener();
|
||||
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
if (plaintext == null) {
|
||||
LOG.info("Failed to decrypt database key");
|
||||
return null;
|
||||
}
|
||||
SecretKey key = new SecretKey(plaintext);
|
||||
// If the DB key was encrypted without using a stored key and a stored
|
||||
// key is now available, re-encrypt the DB key with the stored key
|
||||
if (keyStoreConfig != null &&
|
||||
!crypto.isEncryptedWithStoredKey(ciphertext)) {
|
||||
LOG.info("Re-encrypting database key with stored key");
|
||||
// If the DB key was encrypted with a weak key and a key strengthener
|
||||
// is now available, re-encrypt the DB key with a strengthened key
|
||||
if (keyStrengthener != null &&
|
||||
!crypto.isEncryptedWithStrengthenedKey(ciphertext)) {
|
||||
LOG.info("Re-encrypting database key with strengthened key");
|
||||
encryptAndStoreDatabaseKey(key, password);
|
||||
}
|
||||
return key;
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
@@ -25,28 +25,20 @@ import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStore.Entry;
|
||||
import java.security.KeyStore.SecretKeyEntry;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.lang.System.arraycopy;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
@@ -61,7 +53,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||
private static final byte PBKDF_FORMAT_SCRYPT = 0;
|
||||
private static final byte PBKDF_FORMAT_SCRYPT_KEYSTORE = 1;
|
||||
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
|
||||
|
||||
private final SecureRandom secureRandom;
|
||||
private final PasswordBasedKdf passwordBasedKdf;
|
||||
@@ -322,7 +314,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
@Override
|
||||
public byte[] encryptWithPassword(byte[] input, String password,
|
||||
@Nullable KeyStoreConfig keyStoreConfig) {
|
||||
@Nullable KeyStrengthener keyStrengthener) {
|
||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||
int macBytes = cipher.getMacBytes();
|
||||
// Generate a random salt
|
||||
@@ -332,8 +324,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
int cost = passwordBasedKdf.chooseCostParameter();
|
||||
// Derive the encryption key from the password
|
||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost);
|
||||
if (keyStoreConfig != null)
|
||||
key = requireNonNull(deriveKey(key, keyStoreConfig, true));
|
||||
if (keyStrengthener != null) key = keyStrengthener.strengthenKey(key);
|
||||
// Generate a random IV
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
secureRandom.nextBytes(iv);
|
||||
@@ -344,8 +335,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
byte[] output = new byte[outputLen];
|
||||
int outputOff = 0;
|
||||
// Format version
|
||||
byte formatVersion = keyStoreConfig == null
|
||||
? PBKDF_FORMAT_SCRYPT : PBKDF_FORMAT_SCRYPT_KEYSTORE;
|
||||
byte formatVersion = keyStrengthener == null
|
||||
? PBKDF_FORMAT_SCRYPT : PBKDF_FORMAT_SCRYPT_STRENGTHENED;
|
||||
output[outputOff] = formatVersion;
|
||||
outputOff++;
|
||||
// Salt
|
||||
@@ -367,65 +358,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a key from the given key and another key stored in a keystore.
|
||||
*
|
||||
* @param generateIfMissing Whether the stored key should be generated and
|
||||
* stored if it doesn't already exist
|
||||
* @return The derived key, or null if the stored key doesn't exist and
|
||||
* {@code generateIfMissing} is false
|
||||
*/
|
||||
@Nullable
|
||||
private SecretKey deriveKey(SecretKey key, KeyStoreConfig config,
|
||||
boolean generateIfMissing) {
|
||||
try {
|
||||
// Load the keystore
|
||||
KeyStore ks = KeyStore.getInstance(config.getKeyStoreType());
|
||||
ks.load(null);
|
||||
// Load or generate the stored key
|
||||
javax.crypto.SecretKey storedKey = null;
|
||||
Entry e = ks.getEntry(config.getKeyAlias(), null);
|
||||
if (e == null) {
|
||||
if (!generateIfMissing) {
|
||||
LOG.warning("Key not found in keystore");
|
||||
return null;
|
||||
}
|
||||
// Try the parameter specs in order of preference
|
||||
for (AlgorithmParameterSpec spec : config.getParameterSpecs()) {
|
||||
try {
|
||||
KeyGenerator kg = KeyGenerator.getInstance(
|
||||
config.getMacAlgorithmName(),
|
||||
config.getProviderName());
|
||||
kg.init(spec);
|
||||
storedKey = kg.generateKey();
|
||||
break;
|
||||
} catch (Exception e1) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Could not generate key: " + e1);
|
||||
// Fall back to next spec
|
||||
}
|
||||
}
|
||||
if (storedKey == null) throw new IllegalArgumentException();
|
||||
LOG.info("Stored key in keystore");
|
||||
} else {
|
||||
if (!(e instanceof SecretKeyEntry))
|
||||
throw new IllegalArgumentException();
|
||||
storedKey = ((SecretKeyEntry) e).getSecretKey();
|
||||
LOG.info("Loaded key from keystore");
|
||||
}
|
||||
// Use the input key and the stored key to derive the output key
|
||||
Mac mac = Mac.getInstance(config.getMacAlgorithmName());
|
||||
mac.init(storedKey);
|
||||
return new SecretKey(mac.doFinal(key.getBytes()));
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public byte[] decryptWithPassword(byte[] input, String password,
|
||||
@Nullable KeyStoreConfig keyStoreConfig) {
|
||||
@Nullable KeyStrengthener keyStrengthener) {
|
||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||
int macBytes = cipher.getMacBytes();
|
||||
// The input contains the format version, salt, cost parameter, IV,
|
||||
@@ -439,11 +375,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
inputOff++;
|
||||
// Check whether we support this format version
|
||||
if (formatVersion != PBKDF_FORMAT_SCRYPT &&
|
||||
formatVersion != PBKDF_FORMAT_SCRYPT_KEYSTORE) {
|
||||
formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
|
||||
return null;
|
||||
}
|
||||
boolean useKeyStore = keyStoreConfig != null &&
|
||||
formatVersion == PBKDF_FORMAT_SCRYPT_KEYSTORE;
|
||||
// Salt
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
arraycopy(input, inputOff, salt, 0, salt.length);
|
||||
@@ -459,9 +393,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
inputOff += iv.length;
|
||||
// Derive the decryption key from the password
|
||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
||||
if (useKeyStore) {
|
||||
key = deriveKey(key, keyStoreConfig, false);
|
||||
if (key == null) return null;
|
||||
if (formatVersion == PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
|
||||
if (keyStrengthener == null || !keyStrengthener.isInitialised())
|
||||
return null; // Can't derive the same strengthened key
|
||||
key = keyStrengthener.strengthenKey(key);
|
||||
}
|
||||
// Initialise the cipher
|
||||
try {
|
||||
@@ -481,9 +416,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncryptedWithStoredKey(byte[] ciphertext) {
|
||||
public boolean isEncryptedWithStrengthenedKey(byte[] ciphertext) {
|
||||
return ciphertext.length > 0 &&
|
||||
ciphertext[0] == PBKDF_FORMAT_SCRYPT_KEYSTORE;
|
||||
ciphertext[0] == PBKDF_FORMAT_SCRYPT_STRENGTHENED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.briarproject.bramble.account;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
@@ -40,8 +40,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final DatabaseConfig databaseConfig =
|
||||
context.mock(DatabaseConfig.class);
|
||||
private final KeyStoreConfig keyStoreConfig =
|
||||
context.mock(KeyStoreConfig.class);
|
||||
private final KeyStrengthener keyStrengthener =
|
||||
context.mock(KeyStrengthener.class);
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
private final IdentityManager identityManager =
|
||||
context.mock(IdentityManager.class);
|
||||
@@ -71,8 +71,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(dbDir));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
allowing(databaseConfig).getKeyStoreConfig();
|
||||
will(returnValue(keyStoreConfig));
|
||||
allowing(databaseConfig).getKeyStrengthener();
|
||||
will(returnValue(keyStrengthener));
|
||||
}});
|
||||
|
||||
accountManager =
|
||||
@@ -95,7 +95,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
public void testSignInReturnsFalseIfPasswordIsWrong() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).decryptWithPassword(encryptedKey, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(null));
|
||||
}});
|
||||
|
||||
@@ -116,9 +116,9 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
public void testSignInReturnsTrueIfPasswordIsRight() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).decryptWithPassword(encryptedKey, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(key.getBytes()));
|
||||
oneOf(crypto).isEncryptedWithStoredKey(encryptedKey);
|
||||
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
@@ -142,12 +142,12 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
public void testSignInReEncryptsKey() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).decryptWithPassword(encryptedKey, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(key.getBytes()));
|
||||
oneOf(crypto).isEncryptedWithStoredKey(encryptedKey);
|
||||
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
|
||||
will(returnValue(false));
|
||||
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(newEncryptedKey));
|
||||
}});
|
||||
|
||||
@@ -297,7 +297,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(crypto).generateSecretKey();
|
||||
will(returnValue(key));
|
||||
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(encryptedKey));
|
||||
}});
|
||||
|
||||
@@ -327,7 +327,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).decryptWithPassword(encryptedKey, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(null));
|
||||
}});
|
||||
|
||||
@@ -345,12 +345,12 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(crypto).decryptWithPassword(encryptedKey, password,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(key.getBytes()));
|
||||
oneOf(crypto).isEncryptedWithStoredKey(encryptedKey);
|
||||
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
|
||||
will(returnValue(true));
|
||||
oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword,
|
||||
keyStoreConfig);
|
||||
keyStrengthener);
|
||||
will(returnValue(newEncryptedKey));
|
||||
}});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.KeyStoreConfig;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public KeyStoreConfig getKeyStoreConfig() {
|
||||
public KeyStrengthener getKeyStrengthener() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user