mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-21 07:09:56 +01:00
Erase known copies of keys (unit tests ensure we don't end up using
zeroed keys).
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package net.sf.briar.crypto;
|
package net.sf.briar.crypto;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import net.sf.briar.api.crypto.ErasableKey;
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
import net.sf.briar.util.ByteUtils;
|
import net.sf.briar.util.ByteUtils;
|
||||||
@@ -11,6 +12,8 @@ class ErasableKeyImpl implements ErasableKey {
|
|||||||
|
|
||||||
private final byte[] key;
|
private final byte[] key;
|
||||||
private final String algorithm;
|
private final String algorithm;
|
||||||
|
|
||||||
|
private Collection<byte[]> copies = null;
|
||||||
private boolean erased = false;
|
private boolean erased = false;
|
||||||
|
|
||||||
ErasableKeyImpl(byte[] key, String algorithm) {
|
ErasableKeyImpl(byte[] key, String algorithm) {
|
||||||
@@ -26,6 +29,8 @@ class ErasableKeyImpl implements ErasableKey {
|
|||||||
if(erased) throw new IllegalStateException();
|
if(erased) throw new IllegalStateException();
|
||||||
byte[] b = new byte[key.length];
|
byte[] b = new byte[key.length];
|
||||||
System.arraycopy(key, 0, b, 0, key.length);
|
System.arraycopy(key, 0, b, 0, key.length);
|
||||||
|
if(copies == null) copies = new ArrayList<byte[]>();
|
||||||
|
copies.add(b);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,21 +45,7 @@ class ErasableKeyImpl implements ErasableKey {
|
|||||||
public void erase() {
|
public void erase() {
|
||||||
if(erased) throw new IllegalStateException();
|
if(erased) throw new IllegalStateException();
|
||||||
ByteUtils.erase(key);
|
ByteUtils.erase(key);
|
||||||
|
if(copies != null) for(byte[] b : copies) ByteUtils.erase(b);
|
||||||
erased = true;
|
erased = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
// Not good, but the array can't be used because it's mutable
|
|
||||||
return algorithm.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if(o instanceof ErasableKeyImpl) {
|
|
||||||
ErasableKeyImpl e = (ErasableKeyImpl) o;
|
|
||||||
return algorithm.equals(e.algorithm) && Arrays.equals(key, e.key);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<test name='net.sf.briar.LockFairnessTest'/>
|
<test name='net.sf.briar.LockFairnessTest'/>
|
||||||
<test name='net.sf.briar.ProtocolIntegrationTest'/>
|
<test name='net.sf.briar.ProtocolIntegrationTest'/>
|
||||||
<test name='net.sf.briar.crypto.CounterModeTest'/>
|
<test name='net.sf.briar.crypto.CounterModeTest'/>
|
||||||
|
<test name='net.sf.briar.crypto.ErasableKeyTest'/>
|
||||||
<test name='net.sf.briar.crypto.KeyDerivationTest'/>
|
<test name='net.sf.briar.crypto.KeyDerivationTest'/>
|
||||||
<test name='net.sf.briar.db.BasicH2Test'/>
|
<test name='net.sf.briar.db.BasicH2Test'/>
|
||||||
<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
|
<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
|
||||||
|
|||||||
79
test/net/sf/briar/crypto/ErasableKeyTest.java
Normal file
79
test/net/sf/briar/crypto/ErasableKeyTest.java
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package net.sf.briar.crypto;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import net.sf.briar.api.crypto.ErasableKey;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ErasableKeyTest extends TestCase {
|
||||||
|
|
||||||
|
private static final String CIPHER = "AES";
|
||||||
|
private static final String CIPHER_MODE = "AES/CTR/NoPadding";
|
||||||
|
private static final int IV_BYTES = 16; // 128 bits
|
||||||
|
private static final int KEY_BYTES = 32; // 256 bits
|
||||||
|
private static final String MAC = "HMacSHA256";
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCopiesAreErased() {
|
||||||
|
byte[] master = new byte[KEY_BYTES];
|
||||||
|
random.nextBytes(master);
|
||||||
|
ErasableKey k = new ErasableKeyImpl(master, CIPHER);
|
||||||
|
byte[] copy = k.getEncoded();
|
||||||
|
assertArrayEquals(master, copy);
|
||||||
|
k.erase();
|
||||||
|
byte[] blank = new byte[KEY_BYTES];
|
||||||
|
assertArrayEquals(blank, master);
|
||||||
|
assertArrayEquals(blank, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErasureDoesNotAffectCipher() throws Exception {
|
||||||
|
byte[] key = new byte[KEY_BYTES];
|
||||||
|
random.nextBytes(key);
|
||||||
|
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
|
||||||
|
Cipher c = Cipher.getInstance(CIPHER_MODE);
|
||||||
|
IvParameterSpec iv = new IvParameterSpec(new byte[IV_BYTES]);
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, k, iv);
|
||||||
|
// Encrypt a blank plaintext
|
||||||
|
byte[] plaintext = new byte[123];
|
||||||
|
byte[] ciphertext = c.doFinal(plaintext);
|
||||||
|
// Erase the key and encrypt again - erase() was called after doFinal()
|
||||||
|
k.erase();
|
||||||
|
byte[] ciphertext1 = c.doFinal(plaintext);
|
||||||
|
// Encrypt again - this time erase() was called before doFinal()
|
||||||
|
byte[] ciphertext2 = c.doFinal(plaintext);
|
||||||
|
// The ciphertexts should match
|
||||||
|
assertArrayEquals(ciphertext, ciphertext1);
|
||||||
|
assertArrayEquals(ciphertext, ciphertext2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testErasureDoesNotAffectMac() throws Exception {
|
||||||
|
byte[] key = new byte[KEY_BYTES];
|
||||||
|
random.nextBytes(key);
|
||||||
|
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
|
||||||
|
Mac m = Mac.getInstance(MAC);
|
||||||
|
m.init(k);
|
||||||
|
// Authenticate a blank plaintext
|
||||||
|
byte[] plaintext = new byte[123];
|
||||||
|
byte[] mac = m.doFinal(plaintext);
|
||||||
|
// Erase the key and authenticate again
|
||||||
|
k.erase();
|
||||||
|
byte[] mac1 = m.doFinal(plaintext);
|
||||||
|
// Authenticate again
|
||||||
|
byte[] mac2 = m.doFinal(plaintext);
|
||||||
|
// The MACs should match
|
||||||
|
assertArrayEquals(mac, mac1);
|
||||||
|
assertArrayEquals(mac, mac2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user