mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Merge branch '1696-keystore-crash' into 'master'
Show a dialog instead of crashing if a hardware-backed key can't be loaded Closes #1696 See merge request briar/briar!1233
This commit is contained in:
@@ -2,6 +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.DecryptionException;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
@@ -17,6 +18,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -24,6 +26,7 @@ import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
@@ -95,7 +98,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), "UTF-8"));
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
@@ -147,7 +150,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
@GuardedBy("stateChangeLock")
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes("UTF-8"));
|
||||
out.write(key.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
@@ -192,31 +195,24 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean signIn(String password) {
|
||||
public void signIn(String password) throws DecryptionException {
|
||||
synchronized (stateChangeLock) {
|
||||
SecretKey key = loadAndDecryptDatabaseKey(password);
|
||||
if (key == null) return false;
|
||||
databaseKey = key;
|
||||
return true;
|
||||
databaseKey = loadAndDecryptDatabaseKey(password);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("stateChangeLock")
|
||||
@Nullable
|
||||
private SecretKey loadAndDecryptDatabaseKey(String password) {
|
||||
private SecretKey loadAndDecryptDatabaseKey(String password)
|
||||
throws DecryptionException {
|
||||
String hex = loadEncryptedDatabaseKey();
|
||||
if (hex == null) {
|
||||
LOG.warning("Failed to load encrypted database key");
|
||||
return null;
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
byte[] ciphertext = fromHexString(hex);
|
||||
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener();
|
||||
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
|
||||
keyStrengthener);
|
||||
if (plaintext == null) {
|
||||
LOG.info("Failed to decrypt database key");
|
||||
return null;
|
||||
}
|
||||
SecretKey key = new SecretKey(plaintext);
|
||||
// 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
|
||||
@@ -229,10 +225,11 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changePassword(String oldPassword, String newPassword) {
|
||||
public void changePassword(String oldPassword, String newPassword)
|
||||
throws DecryptionException {
|
||||
synchronized (stateChangeLock) {
|
||||
SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
|
||||
return key != null && encryptAndStoreDatabaseKey(key, newPassword);
|
||||
encryptAndStoreDatabaseKey(key, newPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.DecryptionException;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.KeyStrengthener;
|
||||
@@ -39,6 +40,9 @@ 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.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
|
||||
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;
|
||||
@@ -359,16 +363,17 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public byte[] decryptWithPassword(byte[] input, String password,
|
||||
@Nullable KeyStrengthener keyStrengthener) {
|
||||
@Nullable KeyStrengthener keyStrengthener)
|
||||
throws DecryptionException {
|
||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||
int macBytes = cipher.getMacBytes();
|
||||
// The input contains the format version, salt, cost parameter, IV,
|
||||
// ciphertext and MAC
|
||||
if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES
|
||||
+ STORAGE_IV_BYTES + macBytes)
|
||||
return null; // Invalid input
|
||||
+ STORAGE_IV_BYTES + macBytes) {
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
int inputOff = 0;
|
||||
// Format version
|
||||
byte formatVersion = input[inputOff];
|
||||
@@ -376,7 +381,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// Check whether we support this format version
|
||||
if (formatVersion != PBKDF_FORMAT_SCRYPT &&
|
||||
formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
|
||||
return null;
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
// Salt
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
@@ -385,8 +390,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// Cost parameter
|
||||
long cost = ByteUtils.readUint32(input, inputOff);
|
||||
inputOff += INT_32_BYTES;
|
||||
if (cost < 2 || cost > Integer.MAX_VALUE)
|
||||
return null; // Invalid cost parameter
|
||||
if (cost < 2 || cost > Integer.MAX_VALUE) {
|
||||
throw new DecryptionException(INVALID_CIPHERTEXT);
|
||||
}
|
||||
// IV
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
arraycopy(input, inputOff, iv, 0, iv.length);
|
||||
@@ -394,8 +400,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// Derive the decryption key from the password
|
||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
||||
if (formatVersion == PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
|
||||
if (keyStrengthener == null || !keyStrengthener.isInitialised())
|
||||
return null; // Can't derive the same strengthened key
|
||||
if (keyStrengthener == null || !keyStrengthener.isInitialised()) {
|
||||
// Can't derive the same strengthened key
|
||||
throw new DecryptionException(KEY_STRENGTHENER_ERROR);
|
||||
}
|
||||
key = keyStrengthener.strengthenKey(key);
|
||||
}
|
||||
// Initialise the cipher
|
||||
@@ -411,7 +419,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
cipher.process(input, inputOff, inputLen, output, 0);
|
||||
return output;
|
||||
} catch (GeneralSecurityException e) {
|
||||
return null; // Invalid ciphertext
|
||||
throw new DecryptionException(INVALID_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user