mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
Use scrypt for password-based key derivation.
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
public interface CryptoComponent {
|
public interface CryptoComponent {
|
||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
@@ -124,6 +129,7 @@ public interface CryptoComponent {
|
|||||||
* given password. Returns null if the ciphertext cannot be decrypted and
|
* given password. Returns null if the ciphertext cannot be decrypted and
|
||||||
* authenticated (for example, if the password is wrong).
|
* authenticated (for example, if the password is wrong).
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
byte[] decryptWithPassword(byte[] ciphertext, String password);
|
byte[] decryptWithPassword(byte[] ciphertext, String password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,31 +10,25 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
|||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
import org.spongycastle.crypto.CipherParameters;
|
|
||||||
import org.spongycastle.crypto.CryptoException;
|
import org.spongycastle.crypto.CryptoException;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
import org.spongycastle.crypto.digests.Blake2bDigest;
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
|
|
||||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -43,6 +37,7 @@ import static java.util.logging.Level.INFO;
|
|||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -53,10 +48,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private static final int ED_KEY_PAIR_BITS = 256;
|
private static final int ED_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
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_SALT_BYTES = 32; // 256 bits
|
||||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
|
||||||
private static final int PBKDF_SAMPLES = 30;
|
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
|
private final PasswordBasedKdf passwordBasedKdf;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
@@ -65,7 +59,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private final KeyParser edKeyParser;
|
private final KeyParser edKeyParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider,
|
||||||
|
PasswordBasedKdf passwordBasedKdf) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
SecureRandom defaultSecureRandom = new SecureRandom();
|
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||||
String name = defaultSecureRandom.getProvider().getName();
|
String name = defaultSecureRandom.getProvider().getName();
|
||||||
@@ -85,6 +80,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
secureRandom = new SecureRandom();
|
secureRandom = new SecureRandom();
|
||||||
|
this.passwordBasedKdf = passwordBasedKdf;
|
||||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
PARAMETERS, secureRandom);
|
PARAMETERS, secureRandom);
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
@@ -339,18 +335,18 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
secureRandom.nextBytes(salt);
|
secureRandom.nextBytes(salt);
|
||||||
// Calibrate the KDF
|
// Calibrate the KDF
|
||||||
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
|
int cost = passwordBasedKdf.chooseCostParameter();
|
||||||
// Derive the key from the password
|
// 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
|
// Generate a random IV
|
||||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||||
secureRandom.nextBytes(iv);
|
secureRandom.nextBytes(iv);
|
||||||
// The output contains the salt, iterations, IV, ciphertext and MAC
|
// The output contains the salt, cost parameter, IV, ciphertext and MAC
|
||||||
int outputLen = salt.length + INT_32_BYTES + iv.length + input.length
|
int outputLen = salt.length + INT_32_BYTES + iv.length + input.length
|
||||||
+ macBytes;
|
+ macBytes;
|
||||||
byte[] output = new byte[outputLen];
|
byte[] output = new byte[outputLen];
|
||||||
System.arraycopy(salt, 0, output, 0, salt.length);
|
System.arraycopy(salt, 0, output, 0, salt.length);
|
||||||
ByteUtils.writeUint32(iterations, output, salt.length);
|
ByteUtils.writeUint32(cost, output, salt.length);
|
||||||
System.arraycopy(iv, 0, output, salt.length + INT_32_BYTES, iv.length);
|
System.arraycopy(iv, 0, output, salt.length + INT_32_BYTES, iv.length);
|
||||||
// Initialise the cipher and encrypt the plaintext
|
// Initialise the cipher and encrypt the plaintext
|
||||||
try {
|
try {
|
||||||
@@ -367,19 +363,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public byte[] decryptWithPassword(byte[] input, String password) {
|
public byte[] decryptWithPassword(byte[] input, String password) {
|
||||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
int macBytes = cipher.getMacBytes();
|
int macBytes = cipher.getMacBytes();
|
||||||
// The input contains the salt, iterations, IV, ciphertext and MAC
|
// The input contains the salt, cost parameter, IV, ciphertext and MAC
|
||||||
if (input.length < PBKDF_SALT_BYTES + INT_32_BYTES + STORAGE_IV_BYTES
|
if (input.length < PBKDF_SALT_BYTES + INT_32_BYTES + STORAGE_IV_BYTES
|
||||||
+ macBytes)
|
+ macBytes)
|
||||||
return null; // Invalid input
|
return null; // Invalid input
|
||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
System.arraycopy(input, 0, salt, 0, salt.length);
|
System.arraycopy(input, 0, salt, 0, salt.length);
|
||||||
long iterations = ByteUtils.readUint32(input, salt.length);
|
long cost = ByteUtils.readUint32(input, salt.length);
|
||||||
if (iterations < 0 || iterations > Integer.MAX_VALUE)
|
if (cost < 2 || cost > Integer.MAX_VALUE)
|
||||||
return null; // Invalid iteration count
|
return null; // Invalid cost parameter
|
||||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||||
System.arraycopy(input, salt.length + INT_32_BYTES, iv, 0, iv.length);
|
System.arraycopy(input, salt.length + INT_32_BYTES, iv, 0, iv.length);
|
||||||
// Derive the key from the password
|
// 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
|
// Initialise the cipher
|
||||||
try {
|
try {
|
||||||
cipher.init(false, key, iv);
|
cipher.init(false, key, iv);
|
||||||
@@ -411,64 +407,4 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public String asciiArmour(byte[] b, int lineLength) {
|
public String asciiArmour(byte[] b, int lineLength) {
|
||||||
return AsciiArmour.wrap(b, 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
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CryptoComponent provideCryptoComponent(
|
CryptoComponent provideCryptoComponent(
|
||||||
SecureRandomProvider secureRandomProvider) {
|
SecureRandomProvider secureRandomProvider,
|
||||||
return new CryptoComponentImpl(secureRandomProvider);
|
ScryptKdf passwordBasedKdf) {
|
||||||
|
return new CryptoComponentImpl(secureRandomProvider, passwordBasedKdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@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 = 512; // 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];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public HashTest() {
|
public HashTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
CryptoComponent crypto =
|
CryptoComponent crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class KeyDerivationTest extends BrambleTestCase {
|
public class KeyDerivationTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto =
|
private final CryptoComponent crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
private final TransportCrypto transportCrypto =
|
private final TransportCrypto transportCrypto =
|
||||||
new TransportCryptoImpl(crypto);
|
new TransportCryptoImpl(crypto);
|
||||||
private final TransportId transportId = new TransportId("id");
|
private final TransportId transportId = new TransportId("id");
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() throws Exception {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import static org.junit.Assert.assertFalse;
|
|||||||
public class MacTest extends BrambleTestCase {
|
public class MacTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponent crypto =
|
private final CryptoComponent crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
|
||||||
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
private final SecretKey key1 = getSecretKey(), key2 = getSecretKey();
|
||||||
private final String label1 = getRandomString(123);
|
private final String label1 = getRandomString(123);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.system.SystemClock;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
@@ -8,14 +9,13 @@ import org.junit.Test;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNull;
|
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 =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSecureRandomProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider(),
|
||||||
|
new ScryptKdf(new SystemClock()));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionAndDecryption() {
|
public void testEncryptionAndDecryption() {
|
||||||
@@ -37,17 +37,4 @@ public class PasswordBasedKdfTest extends BrambleTestCase {
|
|||||||
byte[] output = crypto.decryptWithPassword(ciphertext, password);
|
byte[] output = crypto.decryptWithPassword(ciphertext, password);
|
||||||
assertNull(output);
|
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,87 @@
|
|||||||
|
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, 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
byte[] publicKey, byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
SignatureTest() {
|
SignatureTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
KeyPair k = generateKeyPair();
|
KeyPair k = generateKeyPair();
|
||||||
publicKey = k.getPublic().getEncoded();
|
publicKey = k.getPublic().getEncoded();
|
||||||
privateKey = k.getPrivate().getEncoded();
|
privateKey = k.getPrivate().getEncoded();
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.crypto.CryptoModule;
|
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 org.briarproject.bramble.transport.TransportModule;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -10,9 +11,10 @@ import dagger.Component;
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.class,
|
||||||
SyncModule.class,
|
SyncModule.class,
|
||||||
|
SystemModule.class,
|
||||||
TransportModule.class
|
TransportModule.class
|
||||||
})
|
})
|
||||||
interface SyncIntegrationTestComponent {
|
interface SyncIntegrationTestComponent {
|
||||||
|
|||||||
@@ -2,17 +2,14 @@ package org.briarproject.bramble.test;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class TestSeedProviderModule {
|
public class TestSecureRandomModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
SecureRandomProvider provideSeedProvider() {
|
|
||||||
return new TestSecureRandomProvider();
|
return new TestSecureRandomProvider();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.support.design.widget.TextInputEditText;
|
import android.support.design.widget.TextInputEditText;
|
||||||
import android.support.design.widget.TextInputLayout;
|
import android.support.design.widget.TextInputLayout;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
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.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
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.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||||
@@ -107,6 +110,9 @@ public class PasswordFragment extends SetupFragment {
|
|||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
String password = passwordEntry.getText().toString();
|
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.setPassword(password);
|
||||||
setupController.showDozeOrCreateAccount();
|
setupController.showDozeOrCreateAccount();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
|||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
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.test.TestSocksModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
@@ -30,7 +30,7 @@ import dagger.Component;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestDatabaseModule.class,
|
TestDatabaseModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
TestSocksModule.class,
|
TestSocksModule.class,
|
||||||
TestDnsModule.class,
|
TestDnsModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
|||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
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.bramble.transport.TransportModule;
|
||||||
import org.briarproject.briar.blog.BlogModule;
|
import org.briarproject.briar.blog.BlogModule;
|
||||||
import org.briarproject.briar.client.BriarClientModule;
|
import org.briarproject.briar.client.BriarClientModule;
|
||||||
@@ -32,7 +32,7 @@ import dagger.Component;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestDatabaseModule.class,
|
TestDatabaseModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
BlogModule.class,
|
BlogModule.class,
|
||||||
BriarClientModule.class,
|
BriarClientModule.class,
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
|||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||||
import org.briarproject.bramble.test.TestLifecycleModule;
|
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.client.BriarClientModule;
|
||||||
import org.briarproject.briar.forum.ForumModule;
|
import org.briarproject.briar.forum.ForumModule;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ import dagger.Component;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestDatabaseModule.class,
|
TestDatabaseModule.class,
|
||||||
TestLifecycleModule.class,
|
TestLifecycleModule.class,
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
BriarClientModule.class,
|
BriarClientModule.class,
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.class,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
|||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
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.bramble.transport.TransportModule;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
@@ -34,7 +34,7 @@ import dagger.Component;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestDatabaseModule.class,
|
TestDatabaseModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
BriarClientModule.class,
|
BriarClientModule.class,
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
ContactModule.class,
|
ContactModule.class,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.briarproject.bramble.sync.SyncModule;
|
|||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||||
import org.briarproject.bramble.test.TestPluginConfigModule;
|
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.bramble.transport.TransportModule;
|
||||||
import org.briarproject.briar.api.blog.BlogFactory;
|
import org.briarproject.briar.api.blog.BlogFactory;
|
||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
@@ -50,7 +50,7 @@ import dagger.Component;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
TestDatabaseModule.class,
|
TestDatabaseModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSeedProviderModule.class,
|
TestSecureRandomModule.class,
|
||||||
BlogModule.class,
|
BlogModule.class,
|
||||||
BriarClientModule.class,
|
BriarClientModule.class,
|
||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
|
|||||||
Reference in New Issue
Block a user