mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Merge branch 'scrypt' into 'master'
Use scrypt for password-based key derivation See merge request akwizgran/briar!665
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface CryptoComponent {
|
||||
|
||||
SecretKey generateSecretKey();
|
||||
@@ -124,6 +129,7 @@ public interface CryptoComponent {
|
||||
* given password. Returns null if the ciphertext cannot be decrypted and
|
||||
* authenticated (for example, if the password is wrong).
|
||||
*/
|
||||
@Nullable
|
||||
byte[] decryptWithPassword(byte[] ciphertext, String password);
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,39 +10,35 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.spongycastle.crypto.CipherParameters;
|
||||
import org.spongycastle.crypto.CryptoException;
|
||||
import org.spongycastle.crypto.Digest;
|
||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
|
||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
|
||||
@NotNullByDefault
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -53,10 +49,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private static final int ED_KEY_PAIR_BITS = 256;
|
||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||
private static final int PBKDF_SAMPLES = 30;
|
||||
private static final int PBKDF_FORMAT_SCRYPT = 0;
|
||||
|
||||
private final SecureRandom secureRandom;
|
||||
private final PasswordBasedKdf passwordBasedKdf;
|
||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||
@@ -65,7 +61,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
private final KeyParser edKeyParser;
|
||||
|
||||
@Inject
|
||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider,
|
||||
PasswordBasedKdf passwordBasedKdf) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||
String name = defaultSecureRandom.getProvider().getName();
|
||||
@@ -85,6 +82,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
}
|
||||
secureRandom = new SecureRandom();
|
||||
this.passwordBasedKdf = passwordBasedKdf;
|
||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||
PARAMETERS, secureRandom);
|
||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||
@@ -339,23 +337,33 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
secureRandom.nextBytes(salt);
|
||||
// Calibrate the KDF
|
||||
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
|
||||
int cost = passwordBasedKdf.chooseCostParameter();
|
||||
// Derive the key from the password
|
||||
SecretKey key = new SecretKey(pbkdf2(password, salt, iterations));
|
||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost);
|
||||
// Generate a random IV
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
secureRandom.nextBytes(iv);
|
||||
// The output contains the salt, iterations, IV, ciphertext and MAC
|
||||
int outputLen = salt.length + INT_32_BYTES + iv.length + input.length
|
||||
+ macBytes;
|
||||
// The output contains the format version, salt, cost parameter, IV,
|
||||
// ciphertext and MAC
|
||||
int outputLen = 1 + salt.length + INT_32_BYTES + iv.length
|
||||
+ input.length + macBytes;
|
||||
byte[] output = new byte[outputLen];
|
||||
System.arraycopy(salt, 0, output, 0, salt.length);
|
||||
ByteUtils.writeUint32(iterations, output, salt.length);
|
||||
System.arraycopy(iv, 0, output, salt.length + INT_32_BYTES, iv.length);
|
||||
int outputOff = 0;
|
||||
// Format version
|
||||
output[outputOff] = PBKDF_FORMAT_SCRYPT;
|
||||
outputOff++;
|
||||
// Salt
|
||||
System.arraycopy(salt, 0, output, outputOff, salt.length);
|
||||
outputOff += salt.length;
|
||||
// Cost parameter
|
||||
ByteUtils.writeUint32(cost, output, outputOff);
|
||||
outputOff += INT_32_BYTES;
|
||||
// IV
|
||||
System.arraycopy(iv, 0, output, outputOff, iv.length);
|
||||
outputOff += iv.length;
|
||||
// Initialise the cipher and encrypt the plaintext
|
||||
try {
|
||||
cipher.init(true, key, iv);
|
||||
int outputOff = salt.length + INT_32_BYTES + iv.length;
|
||||
cipher.process(input, 0, input.length, output, outputOff);
|
||||
return output;
|
||||
} catch (GeneralSecurityException e) {
|
||||
@@ -364,22 +372,36 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public byte[] decryptWithPassword(byte[] input, String password) {
|
||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||
int macBytes = cipher.getMacBytes();
|
||||
// The input contains the salt, iterations, IV, ciphertext and MAC
|
||||
if (input.length < PBKDF_SALT_BYTES + INT_32_BYTES + STORAGE_IV_BYTES
|
||||
+ macBytes)
|
||||
// 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
|
||||
int inputOff = 0;
|
||||
// Format version
|
||||
byte formatVersion = input[inputOff];
|
||||
inputOff++;
|
||||
if (formatVersion != PBKDF_FORMAT_SCRYPT)
|
||||
return null; // Unknown format
|
||||
// Salt
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
System.arraycopy(input, 0, salt, 0, salt.length);
|
||||
long iterations = ByteUtils.readUint32(input, salt.length);
|
||||
if (iterations < 0 || iterations > Integer.MAX_VALUE)
|
||||
return null; // Invalid iteration count
|
||||
System.arraycopy(input, inputOff, salt, 0, salt.length);
|
||||
inputOff += salt.length;
|
||||
// Cost parameter
|
||||
long cost = ByteUtils.readUint32(input, inputOff);
|
||||
inputOff += INT_32_BYTES;
|
||||
if (cost < 2 || cost > Integer.MAX_VALUE)
|
||||
return null; // Invalid cost parameter
|
||||
// IV
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
System.arraycopy(input, salt.length + INT_32_BYTES, iv, 0, iv.length);
|
||||
System.arraycopy(input, inputOff, iv, 0, iv.length);
|
||||
inputOff += iv.length;
|
||||
// Derive the key from the password
|
||||
SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations));
|
||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
||||
// Initialise the cipher
|
||||
try {
|
||||
cipher.init(false, key, iv);
|
||||
@@ -388,7 +410,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
// Try to decrypt the ciphertext (may be invalid)
|
||||
try {
|
||||
int inputOff = salt.length + INT_32_BYTES + iv.length;
|
||||
int inputLen = input.length - inputOff;
|
||||
byte[] output = new byte[inputLen - macBytes];
|
||||
cipher.process(input, inputOff, inputLen, output, 0);
|
||||
@@ -411,64 +432,4 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
public String asciiArmour(byte[] b, int lineLength) {
|
||||
return AsciiArmour.wrap(b, lineLength);
|
||||
}
|
||||
|
||||
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||
byte[] utf8 = StringUtils.toUtf8(password);
|
||||
Digest digest = new SHA256Digest();
|
||||
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
|
||||
gen.init(utf8, salt, iterations);
|
||||
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||
CipherParameters p = gen.generateDerivedParameters(keyLengthInBits);
|
||||
return ((KeyParameter) p).getKey();
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
int chooseIterationCount(int targetMillis) {
|
||||
List<Long> quickSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||
List<Long> slowSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||
long iterationNanos = 0, initNanos = 0;
|
||||
while (iterationNanos <= 0 || initNanos <= 0) {
|
||||
// Sample the running time with one iteration and two iterations
|
||||
for (int i = 0; i < PBKDF_SAMPLES; i++) {
|
||||
quickSamples.add(sampleRunningTime(1));
|
||||
slowSamples.add(sampleRunningTime(2));
|
||||
}
|
||||
// Calculate the iteration time and the initialisation time
|
||||
long quickMedian = median(quickSamples);
|
||||
long slowMedian = median(slowSamples);
|
||||
iterationNanos = slowMedian - quickMedian;
|
||||
initNanos = quickMedian - iterationNanos;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Init: " + initNanos + ", iteration: "
|
||||
+ iterationNanos);
|
||||
}
|
||||
}
|
||||
long targetNanos = targetMillis * 1000L * 1000L;
|
||||
long iterations = (targetNanos - initNanos) / iterationNanos;
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Target iterations: " + iterations);
|
||||
if (iterations < 1) return 1;
|
||||
if (iterations > Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
||||
return (int) iterations;
|
||||
}
|
||||
|
||||
private long sampleRunningTime(int iterations) {
|
||||
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||
long start = System.nanoTime();
|
||||
Digest digest = new SHA256Digest();
|
||||
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
|
||||
gen.init(password, salt, iterations);
|
||||
gen.generateDerivedParameters(keyLengthInBits);
|
||||
return System.nanoTime() - start;
|
||||
}
|
||||
|
||||
private long median(List<Long> list) {
|
||||
int size = list.size();
|
||||
if (size == 0) throw new IllegalArgumentException();
|
||||
Collections.sort(list);
|
||||
if (size % 2 == 1) return list.get(size / 2);
|
||||
return list.get(size / 2 - 1) + list.get(size / 2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +67,9 @@ public class CryptoModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
CryptoComponent provideCryptoComponent(
|
||||
SecureRandomProvider secureRandomProvider) {
|
||||
return new CryptoComponentImpl(secureRandomProvider);
|
||||
SecureRandomProvider secureRandomProvider,
|
||||
ScryptKdf passwordBasedKdf) {
|
||||
return new CryptoComponentImpl(secureRandomProvider, passwordBasedKdf);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
|
||||
interface PasswordBasedKdf {
|
||||
|
||||
int chooseCostParameter();
|
||||
|
||||
SecretKey deriveKey(String password, byte[] salt, int cost);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.spongycastle.crypto.generators.SCrypt;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
class ScryptKdf implements PasswordBasedKdf {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ScryptKdf.class.getName());
|
||||
|
||||
private static final int MIN_COST = 256; // Min parameter N
|
||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
||||
private static final int BLOCK_SIZE = 8; // Parameter r
|
||||
private static final int PARALLELIZATION = 1; // Parameter p
|
||||
private static final int TARGET_MS = 1000;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
ScryptKdf(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int chooseCostParameter() {
|
||||
// Increase the cost from min to max while measuring performance
|
||||
int cost = MIN_COST;
|
||||
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
||||
cost *= 2;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("KDF cost parameter " + cost);
|
||||
return cost;
|
||||
}
|
||||
|
||||
private long measureDuration(int cost) {
|
||||
byte[] password = new byte[16], salt = new byte[32];
|
||||
long start = clock.currentTimeMillis();
|
||||
SCrypt.generate(password, salt, cost, BLOCK_SIZE, PARALLELIZATION,
|
||||
SecretKey.LENGTH);
|
||||
return clock.currentTimeMillis() - start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String password, byte[] salt, int cost) {
|
||||
long start = System.currentTimeMillis();
|
||||
byte[] passwordBytes = StringUtils.toUtf8(password);
|
||||
SecretKey k = new SecretKey(SCrypt.generate(passwordBytes, salt, cost,
|
||||
BLOCK_SIZE, PARALLELIZATION, SecretKey.LENGTH));
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Deriving key from password took " + duration + " ms");
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ public class HashTest extends BrambleTestCase {
|
||||
private final byte[] inputBytes2 = new byte[0];
|
||||
|
||||
public HashTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -18,7 +18,7 @@ public class KeyAgreementTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testDeriveSharedSecret() throws Exception {
|
||||
CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||
Random random = new Random();
|
||||
|
||||
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class KeyDerivationTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
private final TransportCrypto transportCrypto =
|
||||
new TransportCryptoImpl(crypto);
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
|
||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
|
||||
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponentImpl crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
|
||||
@Test
|
||||
public void testAgreementPublicKeyLength() throws Exception {
|
||||
|
||||
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertFalse;
|
||||
public class MacTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponent crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
|
||||
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||
private final String label1 = getRandomString(123);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.system.SystemClock;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
@@ -8,14 +9,13 @@ import org.junit.Test;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PasswordBasedKdfTest extends BrambleTestCase {
|
||||
public class PasswordBasedEncryptionTest extends BrambleTestCase {
|
||||
|
||||
private final CryptoComponentImpl crypto =
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
new CryptoComponentImpl(new TestSecureRandomProvider(),
|
||||
new ScryptKdf(new SystemClock()));
|
||||
|
||||
@Test
|
||||
public void testEncryptionAndDecryption() {
|
||||
@@ -37,17 +37,4 @@ public class PasswordBasedKdfTest extends BrambleTestCase {
|
||||
byte[] output = crypto.decryptWithPassword(ciphertext, password);
|
||||
assertNull(output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalibration() {
|
||||
// If the target time is unachievable, one iteration should be used
|
||||
int iterations = crypto.chooseIterationCount(0);
|
||||
assertEquals(1, iterations);
|
||||
// If the target time is long, more than one iteration should be used
|
||||
iterations = crypto.chooseIterationCount(10 * 1000);
|
||||
assertTrue(iterations > 1);
|
||||
// If the target time is very long, max iterations should be used
|
||||
iterations = crypto.chooseIterationCount(Integer.MAX_VALUE);
|
||||
assertEquals(Integer.MAX_VALUE, iterations);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.briarproject.bramble.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.system.SystemClock;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ScryptKdfTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testPasswordAffectsKey() throws Exception {
|
||||
PasswordBasedKdf kdf = new ScryptKdf(new SystemClock());
|
||||
byte[] salt = getRandomBytes(32);
|
||||
Set<Bytes> keys = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
String password = getRandomString(16);
|
||||
SecretKey key = kdf.deriveKey(password, salt, 256);
|
||||
assertTrue(keys.add(new Bytes(key.getBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaltAffectsKey() throws Exception {
|
||||
PasswordBasedKdf kdf = new ScryptKdf(new SystemClock());
|
||||
String password = getRandomString(16);
|
||||
Set<Bytes> keys = new HashSet<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte[] salt = getRandomBytes(32);
|
||||
SecretKey key = kdf.deriveKey(password, salt, 256);
|
||||
assertTrue(keys.add(new Bytes(key.getBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCostParameterAffectsKey() throws Exception {
|
||||
PasswordBasedKdf kdf = new ScryptKdf(new SystemClock());
|
||||
String password = getRandomString(16);
|
||||
byte[] salt = getRandomBytes(32);
|
||||
Set<Bytes> keys = new HashSet<>();
|
||||
for (int cost = 2; cost <= 256; cost *= 2) {
|
||||
SecretKey key = kdf.deriveKey(password, salt, cost);
|
||||
assertTrue(keys.add(new Bytes(key.getBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalibration() throws Exception {
|
||||
Clock clock = new ArrayClock(
|
||||
0, 50, // Duration for cost 256
|
||||
0, 100, // Duration for cost 512
|
||||
0, 200, // Duration for cost 1024
|
||||
0, 400, // Duration for cost 2048
|
||||
0, 800 // Duration for cost 4096
|
||||
);
|
||||
PasswordBasedKdf kdf = new ScryptKdf(clock);
|
||||
assertEquals(4096, kdf.chooseCostParameter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalibrationChoosesMinCost() throws Exception {
|
||||
Clock clock = new ArrayClock(
|
||||
0, 2000 // Duration for cost 256 is already too high
|
||||
);
|
||||
PasswordBasedKdf kdf = new ScryptKdf(clock);
|
||||
assertEquals(256, kdf.chooseCostParameter());
|
||||
}
|
||||
|
||||
private static class ArrayClock implements Clock {
|
||||
|
||||
private final long[] times;
|
||||
private int index = 0;
|
||||
|
||||
private ArrayClock(long... times) {
|
||||
this.times = times;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long currentTimeMillis() {
|
||||
return times[index++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sleep(long milliseconds) throws InterruptedException {
|
||||
Thread.sleep(milliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public abstract class SignatureTest extends BrambleTestCase {
|
||||
byte[] publicKey, byte[] signature) throws GeneralSecurityException;
|
||||
|
||||
SignatureTest() {
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||
KeyPair k = generateKeyPair();
|
||||
publicKey = k.getPublic().getEncoded();
|
||||
privateKey = k.getPrivate().getEncoded();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.crypto.CryptoModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
@@ -10,9 +11,10 @@ import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
CryptoModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
})
|
||||
interface SyncIntegrationTestComponent {
|
||||
|
||||
@@ -2,17 +2,14 @@ package org.briarproject.bramble.test;
|
||||
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class TestSeedProviderModule {
|
||||
public class TestSecureRandomModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
SecureRandomProvider provideSeedProvider() {
|
||||
SecureRandomProvider provideSecureRandomProvider() {
|
||||
return new TestSecureRandomProvider();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.design.widget.TextInputEditText;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
@@ -13,6 +15,7 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||
@@ -107,6 +110,9 @@ public class PasswordFragment extends SetupFragment {
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
String password = passwordEntry.getText().toString();
|
||||
IBinder token = passwordEntry.getWindowToken();
|
||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
||||
setupController.setPassword(password);
|
||||
setupController.showDozeOrCreateAccount();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.test.TestSocksModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
@@ -30,7 +30,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
TestSocksModule.class,
|
||||
TestDnsModule.class,
|
||||
LifecycleModule.class,
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.briar.blog.BlogModule;
|
||||
import org.briarproject.briar.client.BriarClientModule;
|
||||
@@ -32,7 +32,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
BlogModule.class,
|
||||
BriarClientModule.class,
|
||||
ClientModule.class,
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.bramble.test.TestLifecycleModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.briar.client.BriarClientModule;
|
||||
import org.briarproject.briar.forum.ForumModule;
|
||||
|
||||
@@ -22,7 +22,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestLifecycleModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
BriarClientModule.class,
|
||||
ClientModule.class,
|
||||
CryptoModule.class,
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||
@@ -34,7 +34,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
BriarClientModule.class,
|
||||
ClientModule.class,
|
||||
ContactModule.class,
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
||||
import org.briarproject.bramble.system.SystemModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||
import org.briarproject.bramble.test.TestSeedProviderModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.transport.TransportModule;
|
||||
import org.briarproject.briar.api.blog.BlogFactory;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
@@ -50,7 +50,7 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
BlogModule.class,
|
||||
BriarClientModule.class,
|
||||
ClientModule.class,
|
||||
|
||||
Reference in New Issue
Block a user