mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Use the Fortuna generator instead of the JVM's SecureRandom. Bug #4.
Note that this is only the generator part of Fortuna, not the accumulator. The generator requires a seed, which is provided by a platform-specific implementation of SeedProvider. On Linux the implementation reads the seed from /dev/urandom.
This commit is contained in:
12
briar-api/src/org/briarproject/api/crypto/SeedProvider.java
Normal file
12
briar-api/src/org/briarproject/api/crypto/SeedProvider.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package org.briarproject.api.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses a platform-specific source to provide a seed for a pseudo-random
|
||||||
|
* number generator.
|
||||||
|
*/
|
||||||
|
public interface SeedProvider {
|
||||||
|
|
||||||
|
int SEED_BYTES = 32;
|
||||||
|
|
||||||
|
byte[] getSeed();
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.briarproject.api.crypto.AuthenticatedCipher;
|
import org.briarproject.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.KeyPair;
|
import org.briarproject.api.crypto.KeyPair;
|
||||||
@@ -28,9 +30,9 @@ import org.briarproject.api.crypto.PrivateKey;
|
|||||||
import org.briarproject.api.crypto.PseudoRandom;
|
import org.briarproject.api.crypto.PseudoRandom;
|
||||||
import org.briarproject.api.crypto.PublicKey;
|
import org.briarproject.api.crypto.PublicKey;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.api.crypto.SeedProvider;
|
||||||
import org.briarproject.api.crypto.Signature;
|
import org.briarproject.api.crypto.Signature;
|
||||||
import org.briarproject.util.ByteUtils;
|
import org.briarproject.util.ByteUtils;
|
||||||
|
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
import org.spongycastle.crypto.BlockCipher;
|
||||||
import org.spongycastle.crypto.CipherParameters;
|
import org.spongycastle.crypto.CipherParameters;
|
||||||
@@ -86,23 +88,25 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// Blank secret for argument validation
|
// Blank secret for argument validation
|
||||||
private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES];
|
private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES];
|
||||||
|
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||||
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
|
|
||||||
CryptoComponentImpl() {
|
@Inject
|
||||||
agreementKeyParser = new Sec1KeyParser(PARAMETERS, P,
|
CryptoComponentImpl(SeedProvider r) {
|
||||||
AGREEMENT_KEY_PAIR_BITS);
|
if(!FortunaSecureRandom.selfTest()) throw new RuntimeException();
|
||||||
signatureKeyParser = new Sec1KeyParser(PARAMETERS, P,
|
secureRandom = new FortunaSecureRandom(r.getSeed());
|
||||||
SIGNATURE_KEY_PAIR_BITS);
|
|
||||||
secureRandom = new SecureRandom();
|
|
||||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
PARAMETERS, secureRandom);
|
PARAMETERS, secureRandom);
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
agreementKeyPairGenerator.init(params);
|
agreementKeyPairGenerator.init(params);
|
||||||
signatureKeyPairGenerator = new ECKeyPairGenerator();
|
signatureKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
signatureKeyPairGenerator.init(params);
|
signatureKeyPairGenerator.init(params);
|
||||||
|
agreementKeyParser = new Sec1KeyParser(PARAMETERS, P,
|
||||||
|
AGREEMENT_KEY_PAIR_BITS);
|
||||||
|
signatureKeyParser = new Sec1KeyParser(PARAMETERS, P,
|
||||||
|
SIGNATURE_KEY_PAIR_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import javax.inject.Singleton;
|
|||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.CryptoExecutor;
|
import org.briarproject.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.api.crypto.PasswordStrengthEstimator;
|
||||||
|
import org.briarproject.api.crypto.SeedProvider;
|
||||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.util.OsUtils;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
@@ -39,6 +41,9 @@ public class CryptoModule extends AbstractModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
|
if(OsUtils.isAndroid() || OsUtils.isLinux()) {
|
||||||
|
bind(SeedProvider.class).to(LinuxSeedProvider.class);
|
||||||
|
}
|
||||||
bind(CryptoComponent.class).to(
|
bind(CryptoComponent.class).to(
|
||||||
CryptoComponentImpl.class).in(Singleton.class);
|
CryptoComponentImpl.class).in(Singleton.class);
|
||||||
bind(PasswordStrengthEstimator.class).to(
|
bind(PasswordStrengthEstimator.class).to(
|
||||||
|
|||||||
83
briar-core/src/org/briarproject/crypto/FortunaGenerator.java
Normal file
83
briar-core/src/org/briarproject/crypto/FortunaGenerator.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.MessageDigest;
|
||||||
|
import org.spongycastle.crypto.BlockCipher;
|
||||||
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
|
import org.spongycastle.crypto.engines.AESLightEngine;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the Fortuna pseudo-random number generator, as described in
|
||||||
|
* Ferguson and Schneier, <i>Practical Cryptography</i>, chapter 9.
|
||||||
|
*/
|
||||||
|
class FortunaGenerator {
|
||||||
|
|
||||||
|
private static final int MAX_BYTES_PER_REQUEST = 1024 * 1024;
|
||||||
|
private static final int KEY_BYTES = 32;
|
||||||
|
private static final int BLOCK_BYTES = 16;
|
||||||
|
|
||||||
|
// All of the following are locking: this
|
||||||
|
private final MessageDigest digest = new DoubleDigest(new SHA256Digest());
|
||||||
|
private final BlockCipher cipher = new AESLightEngine();
|
||||||
|
private final byte[] key = new byte[KEY_BYTES];
|
||||||
|
private final byte[] counter = new byte[BLOCK_BYTES];
|
||||||
|
private final byte[] buffer = new byte[BLOCK_BYTES];
|
||||||
|
private final byte[] newKey = new byte[KEY_BYTES];
|
||||||
|
|
||||||
|
FortunaGenerator(byte[] seed) {
|
||||||
|
reseed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void reseed(byte[] seed) {
|
||||||
|
digest.update(key);
|
||||||
|
digest.update(seed);
|
||||||
|
digest.digest(key, 0, KEY_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
synchronized void incrementCounter() {
|
||||||
|
counter[0]++;
|
||||||
|
for(int i = 0; counter[i] == 0; i++) {
|
||||||
|
if(i + 1 == BLOCK_BYTES)
|
||||||
|
throw new RuntimeException("Counter exhausted");
|
||||||
|
counter[i + 1]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
synchronized byte[] getCounter() {
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized int nextBytes(byte[] dest, int off, int len) {
|
||||||
|
// Don't write more than the maximum number of bytes in one request
|
||||||
|
if(len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
|
||||||
|
cipher.init(true, new KeyParameter(key));
|
||||||
|
// Generate full blocks directly into the output buffer
|
||||||
|
int fullBlocks = len / BLOCK_BYTES;
|
||||||
|
for(int i = 0; i < fullBlocks; i++) {
|
||||||
|
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
// Generate a partial block if needed
|
||||||
|
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
|
||||||
|
assert remaining < BLOCK_BYTES;
|
||||||
|
if(remaining > 0) {
|
||||||
|
cipher.processBlock(counter, 0, buffer, 0);
|
||||||
|
incrementCounter();
|
||||||
|
// Copy the partial block to the output buffer and erase our copy
|
||||||
|
System.arraycopy(buffer, 0, dest, off + done, remaining);
|
||||||
|
for(int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
|
||||||
|
}
|
||||||
|
// Generate a new key
|
||||||
|
for(int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
|
||||||
|
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
|
||||||
|
incrementCounter();
|
||||||
|
}
|
||||||
|
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
|
||||||
|
for(int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
|
||||||
|
// Return the number of bytes written
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link java.security.SecureRandom SecureRandom} implementation based on a
|
||||||
|
* {@link FortunaGenerator}.
|
||||||
|
*/
|
||||||
|
class FortunaSecureRandom extends SecureRandom {
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
static final byte[] SELF_TEST_VECTOR_1 =
|
||||||
|
StringUtils.fromHexString("4BD6EA599D47E3EE9DD911833C29CA22");
|
||||||
|
static final byte[] SELF_TEST_VECTOR_2 =
|
||||||
|
StringUtils.fromHexString("10984D576E6850E505CA9F42A9BFD88A");
|
||||||
|
static final byte[] SELF_TEST_VECTOR_3 =
|
||||||
|
StringUtils.fromHexString("1E12DA166BD86DCECDE50A8296018DE2");
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -417332227850184134L;
|
||||||
|
private static final Provider PROVIDER = new FortunaProvider();
|
||||||
|
|
||||||
|
FortunaSecureRandom(byte[] seed) {
|
||||||
|
super(new FortunaSecureRandomSpi(seed), PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the {@link #nextBytes(byte[])} and {@link #setSeed(byte[])}
|
||||||
|
* methods are passed through to the generator in the expected way.
|
||||||
|
*/
|
||||||
|
static boolean selfTest() {
|
||||||
|
byte[] seed = new byte[32];
|
||||||
|
SecureRandom r = new FortunaSecureRandom(seed);
|
||||||
|
byte[] output = new byte[16];
|
||||||
|
r.nextBytes(output);
|
||||||
|
if(!Arrays.equals(SELF_TEST_VECTOR_1, output)) return false;
|
||||||
|
r.nextBytes(output);
|
||||||
|
if(!Arrays.equals(SELF_TEST_VECTOR_2, output)) return false;
|
||||||
|
r.setSeed(seed);
|
||||||
|
r.nextBytes(output);
|
||||||
|
if(!Arrays.equals(SELF_TEST_VECTOR_3, output)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FortunaSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1677799887497202351L;
|
||||||
|
|
||||||
|
private final FortunaGenerator generator;
|
||||||
|
|
||||||
|
private FortunaSecureRandomSpi(byte[] seed) {
|
||||||
|
generator = new FortunaGenerator(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int numBytes) {
|
||||||
|
byte[] b = new byte[numBytes];
|
||||||
|
engineNextBytes(b);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] b) {
|
||||||
|
int offset = 0;
|
||||||
|
while(offset < b.length)
|
||||||
|
offset += generator.nextBytes(b, offset, b.length - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] seed) {
|
||||||
|
generator.reseed(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FortunaProvider extends Provider {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -833121797778381769L;
|
||||||
|
|
||||||
|
private FortunaProvider() {
|
||||||
|
super("Fortuna", 1.0, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.SeedProvider;
|
||||||
|
|
||||||
|
class LinuxSeedProvider implements SeedProvider {
|
||||||
|
|
||||||
|
public byte[] getSeed() {
|
||||||
|
byte[] seed = new byte[SEED_BYTES];
|
||||||
|
try {
|
||||||
|
DataInputStream in = new DataInputStream(
|
||||||
|
new FileInputStream("/dev/urandom"));
|
||||||
|
in.readFully(seed);
|
||||||
|
in.close();
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,5 +12,6 @@
|
|||||||
<classpathentry kind="lib" path="libs/jmock-2.5.1.jar"/>
|
<classpathentry kind="lib" path="libs/jmock-2.5.1.jar"/>
|
||||||
<classpathentry kind="lib" path="libs/junit-4.9b3.jar"/>
|
<classpathentry kind="lib" path="libs/junit-4.9b3.jar"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||||
|
<classpathentry kind="lib" path="/briar-core/libs/sc-light-jdk15on-1.47.0.3-SNAPSHOT.jar" sourcepath="/briar-core/libs/source/sc-light-jdk15on-1.47.0.3-SNAPSHOT-source.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -93,6 +93,8 @@
|
|||||||
<jvmarg value='-Djava.library.path=../briar-desktop/libs'/>
|
<jvmarg value='-Djava.library.path=../briar-desktop/libs'/>
|
||||||
<test name='org.briarproject.LockFairnessTest'/>
|
<test name='org.briarproject.LockFairnessTest'/>
|
||||||
<test name='org.briarproject.ProtocolIntegrationTest'/>
|
<test name='org.briarproject.ProtocolIntegrationTest'/>
|
||||||
|
<test name='org.briarproject.crypto.FortunaGeneratorTest'/>
|
||||||
|
<test name='org.briarproject.crypto.FortunaSecureRandomTest'/>
|
||||||
<test name='org.briarproject.crypto.KeyAgreementTest'/>
|
<test name='org.briarproject.crypto.KeyAgreementTest'/>
|
||||||
<test name='org.briarproject.crypto.KeyDerivationTest'/>
|
<test name='org.briarproject.crypto.KeyDerivationTest'/>
|
||||||
<test name='org.briarproject.crypto.KeyEncodingAndParsingTest'/>
|
<test name='org.briarproject.crypto.KeyEncodingAndParsingTest'/>
|
||||||
|
|||||||
16
briar-tests/src/org/briarproject/TestSeedProvider.java
Normal file
16
briar-tests/src/org/briarproject/TestSeedProvider.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.SeedProvider;
|
||||||
|
|
||||||
|
public class TestSeedProvider implements SeedProvider {
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
public byte[] getSeed() {
|
||||||
|
byte[] seed = new byte[32];
|
||||||
|
random.nextBytes(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.spongycastle.crypto.BlockCipher;
|
||||||
|
import org.spongycastle.crypto.engines.AESLightEngine;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
public class FortunaGeneratorTest extends BriarTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCounterInitialisedToOne() {
|
||||||
|
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
||||||
|
// The counter is little-endian
|
||||||
|
byte[] expected = new byte[16];
|
||||||
|
expected[0] = 1;
|
||||||
|
assertArrayEquals(expected, f.getCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrementCounter() {
|
||||||
|
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
||||||
|
// Increment the counter until it reaches 255
|
||||||
|
for(int i = 1; i < 255; i++) f.incrementCounter();
|
||||||
|
byte[] expected = new byte[16];
|
||||||
|
expected[0] = (byte) 255;
|
||||||
|
assertArrayEquals(expected, f.getCounter());
|
||||||
|
// Increment the counter again - it should carry into the next byte
|
||||||
|
f.incrementCounter();
|
||||||
|
expected[0] = 0;
|
||||||
|
expected[1] = 1;
|
||||||
|
assertArrayEquals(expected, f.getCounter());
|
||||||
|
// Increment the counter until it carries into the next byte
|
||||||
|
for(int i = 256; i < 65536; i++) f.incrementCounter();
|
||||||
|
expected[0] = 0;
|
||||||
|
expected[1] = 0;
|
||||||
|
expected[2] = 1;
|
||||||
|
assertArrayEquals(expected, f.getCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNextBytes() {
|
||||||
|
// Generate several outputs with the same seed - they should all match
|
||||||
|
byte[] seed = new byte[32];
|
||||||
|
byte[] out1 = new byte[48];
|
||||||
|
new FortunaGenerator(seed).nextBytes(out1, 0, 48);
|
||||||
|
// One byte longer than a block, with an offset of one
|
||||||
|
byte[] out2 = new byte[49];
|
||||||
|
new FortunaGenerator(seed).nextBytes(out2, 1, 48);
|
||||||
|
for(int i = 0; i < 48; i++) assertEquals(out1[i], out2[i + 1]);
|
||||||
|
// One byte shorter than a block
|
||||||
|
byte[] out3 = new byte[47];
|
||||||
|
new FortunaGenerator(seed).nextBytes(out3, 0, 47);
|
||||||
|
for(int i = 0; i < 47; i++) assertEquals(out1[i], out3[i]);
|
||||||
|
// Less than a block, with an offset greater than a block
|
||||||
|
byte[] out4 = new byte[32];
|
||||||
|
new FortunaGenerator(seed).nextBytes(out4, 17, 15);
|
||||||
|
for(int i = 0; i < 15; i++) assertEquals(out1[i], out4[i + 17]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRekeying() {
|
||||||
|
byte[] seed = new byte[32];
|
||||||
|
FortunaGenerator f = new FortunaGenerator(seed);
|
||||||
|
// Generate three blocks of output
|
||||||
|
byte[] out1 = new byte[48];
|
||||||
|
f.nextBytes(out1, 0, 48);
|
||||||
|
// Create another generator with the same seed and generate one block
|
||||||
|
f = new FortunaGenerator(seed);
|
||||||
|
byte[] out2 = new byte[16];
|
||||||
|
f.nextBytes(out2, 0, 16);
|
||||||
|
// The generator should have rekeyed with the 2nd and 3rd blocks
|
||||||
|
byte[] expectedKey = new byte[32];
|
||||||
|
System.arraycopy(out1, 16, expectedKey, 0, 32);
|
||||||
|
// The generator's counter should have been incremented 3 times
|
||||||
|
byte[] expectedCounter = new byte[16];
|
||||||
|
expectedCounter[0] = 4;
|
||||||
|
// The next expected output block is the counter encrypted with the key
|
||||||
|
byte[] expectedOutput = new byte[16];
|
||||||
|
BlockCipher c = new AESLightEngine();
|
||||||
|
c.init(true, new KeyParameter(expectedKey));
|
||||||
|
c.processBlock(expectedCounter, 0, expectedOutput, 0);
|
||||||
|
// Check that the generator produces the expected output block
|
||||||
|
byte[] out3 = new byte[16];
|
||||||
|
f.nextBytes(out3, 0, 16);
|
||||||
|
assertArrayEquals(expectedOutput, out3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaximumRequestLength() {
|
||||||
|
int expectedMax = 1024 * 1024;
|
||||||
|
byte[] output = new byte[expectedMax + 123];
|
||||||
|
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
||||||
|
assertEquals(expectedMax, f.nextBytes(output, 0, output.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
|
import static org.briarproject.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_1;
|
||||||
|
import static org.briarproject.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_2;
|
||||||
|
import static org.briarproject.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_3;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.api.crypto.MessageDigest;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.spongycastle.crypto.BlockCipher;
|
||||||
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
|
import org.spongycastle.crypto.engines.AESLightEngine;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
public class FortunaSecureRandomTest extends BriarTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassPassesSelfTest() {
|
||||||
|
assertTrue(FortunaSecureRandom.selfTest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelfTestVectorsAreReproducible() {
|
||||||
|
byte[] key = new byte[32], seed = new byte[32];
|
||||||
|
byte[] counter = new byte[16], output = new byte[16];
|
||||||
|
byte[] newKey = new byte[32];
|
||||||
|
// Calculate the initial key
|
||||||
|
MessageDigest digest = new DoubleDigest(new SHA256Digest());
|
||||||
|
digest.update(key);
|
||||||
|
digest.update(seed);
|
||||||
|
digest.digest(key, 0, 32);
|
||||||
|
// Calculate the first output block and the new key
|
||||||
|
BlockCipher c = new AESLightEngine();
|
||||||
|
c.init(true, new KeyParameter(key));
|
||||||
|
counter[0] = 1;
|
||||||
|
c.processBlock(counter, 0, output, 0);
|
||||||
|
counter[0] = 2;
|
||||||
|
c.processBlock(counter, 0, newKey, 0);
|
||||||
|
counter[0] = 3;
|
||||||
|
c.processBlock(counter, 0, newKey, 16);
|
||||||
|
System.arraycopy(newKey, 0, key, 0, 32);
|
||||||
|
// The first self-test vector should match the first output block
|
||||||
|
assertArrayEquals(SELF_TEST_VECTOR_1, output);
|
||||||
|
// Calculate the second output block and the new key before reseeding
|
||||||
|
c.init(true, new KeyParameter(key));
|
||||||
|
counter[0] = 4;
|
||||||
|
c.processBlock(counter, 0, output, 0);
|
||||||
|
counter[0] = 5;
|
||||||
|
c.processBlock(counter, 0, newKey, 0);
|
||||||
|
counter[0] = 6;
|
||||||
|
c.processBlock(counter, 0, newKey, 16);
|
||||||
|
System.arraycopy(newKey, 0, key, 0, 32);
|
||||||
|
// The second self-test vector should match the second output block
|
||||||
|
assertArrayEquals(SELF_TEST_VECTOR_2, output);
|
||||||
|
// Calculate the new key after reseeding
|
||||||
|
digest.update(key);
|
||||||
|
digest.update(seed);
|
||||||
|
digest.digest(key, 0, 32);
|
||||||
|
// Calculate the third output block
|
||||||
|
c.init(true, new KeyParameter(key));
|
||||||
|
counter[0] = 8;
|
||||||
|
c.processBlock(counter, 0, output, 0);
|
||||||
|
// The third self-test vector should match the third output block
|
||||||
|
assertArrayEquals(SELF_TEST_VECTOR_3, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
package org.briarproject.crypto;
|
package org.briarproject.crypto;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestSeedProvider;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.KeyPair;
|
import org.briarproject.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.api.crypto.SeedProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class KeyAgreementTest extends BriarTestCase {
|
public class KeyAgreementTest extends BriarTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKeyAgreement() throws Exception {
|
public void testKeyAgreement() throws Exception {
|
||||||
CryptoComponent crypto = new CryptoComponentImpl();
|
SeedProvider seedProvider = new TestSeedProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import java.util.List;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestSeedProvider;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BriarTestCase {
|
public class KeyDerivationTest extends BriarTestCase {
|
||||||
@@ -17,7 +17,7 @@ public class KeyDerivationTest extends BriarTestCase {
|
|||||||
private final byte[] secret;
|
private final byte[] secret;
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
public KeyDerivationTest() {
|
||||||
crypto = new CryptoComponentImpl();
|
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
||||||
secret = new byte[32];
|
secret = new byte[32];
|
||||||
new Random().nextBytes(secret);
|
new Random().nextBytes(secret);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestSeedProvider;
|
||||||
import org.briarproject.api.crypto.KeyPair;
|
import org.briarproject.api.crypto.KeyPair;
|
||||||
import org.briarproject.api.crypto.KeyParser;
|
import org.briarproject.api.crypto.KeyParser;
|
||||||
import org.briarproject.api.crypto.PrivateKey;
|
import org.briarproject.api.crypto.PrivateKey;
|
||||||
@@ -15,7 +16,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
public class KeyEncodingAndParsingTest extends BriarTestCase {
|
public class KeyEncodingAndParsingTest extends BriarTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto = new CryptoComponentImpl();
|
private final CryptoComponentImpl crypto =
|
||||||
|
new CryptoComponentImpl(new TestSeedProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyEncodingAndParsing() throws Exception {
|
public void testAgreementPublicKeyEncodingAndParsing() throws Exception {
|
||||||
|
|||||||
@@ -5,15 +5,16 @@ import static org.junit.Assert.assertArrayEquals;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.briarproject.BriarTestCase;
|
import org.briarproject.BriarTestCase;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.TestSeedProvider;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class PasswordBasedKdfTest extends BriarTestCase {
|
public class PasswordBasedKdfTest extends BriarTestCase {
|
||||||
|
|
||||||
|
private final CryptoComponentImpl crypto =
|
||||||
|
new CryptoComponentImpl(new TestSeedProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionAndDecryption() {
|
public void testEncryptionAndDecryption() {
|
||||||
CryptoComponent crypto = new CryptoComponentImpl();
|
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
byte[] input = new byte[1234];
|
byte[] input = new byte[1234];
|
||||||
random.nextBytes(input);
|
random.nextBytes(input);
|
||||||
@@ -25,7 +26,6 @@ public class PasswordBasedKdfTest extends BriarTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidCiphertextReturnsNull() {
|
public void testInvalidCiphertextReturnsNull() {
|
||||||
CryptoComponent crypto = new CryptoComponentImpl();
|
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
byte[] input = new byte[1234];
|
byte[] input = new byte[1234];
|
||||||
random.nextBytes(input);
|
random.nextBytes(input);
|
||||||
@@ -41,7 +41,6 @@ public class PasswordBasedKdfTest extends BriarTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalibration() {
|
public void testCalibration() {
|
||||||
CryptoComponentImpl crypto = new CryptoComponentImpl();
|
|
||||||
// If the target time is unachievable, one iteration should be used
|
// If the target time is unachievable, one iteration should be used
|
||||||
int iterations = crypto.chooseIterationCount(0);
|
int iterations = crypto.chooseIterationCount(0);
|
||||||
assertEquals(1, iterations);
|
assertEquals(1, iterations);
|
||||||
|
|||||||
Reference in New Issue
Block a user