mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 19:59:05 +01:00
Don't try to erase secrets from memory.
1. The things we're really trying to protect - contact identities, message contents, etc - can't be erased from memory because they're encapsulated inside objects we don't control. 2. Long-term secrets can't be protected by erasing them from memory because they're stored in the database and the database key has to be held in memory whenever the app's running. 3. If the runtime uses a compacting garbage collector then we have no way to ensure an object is erased from memory. 4. Trying to erase secrets from memory makes the code more complex. Conclusion: Let's not try to protect secrets from an attacker who can read arbitrary memory locations.
This commit is contained in:
@@ -38,7 +38,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
|
||||
|
||||
public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad)
|
||||
throws GeneralSecurityException {
|
||||
KeyParameter k = new KeyParameter(key.getEncoded());
|
||||
KeyParameter k = new KeyParameter(key.getBytes());
|
||||
AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad);
|
||||
try {
|
||||
cipher.init(encrypt, params);
|
||||
|
||||
@@ -7,8 +7,6 @@ import static org.briarproject.crypto.EllipticCurveConstants.P;
|
||||
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
|
||||
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
@@ -31,6 +29,7 @@ import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.system.SeedProvider;
|
||||
import org.briarproject.util.ByteUtils;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.spongycastle.crypto.BlockCipher;
|
||||
import org.spongycastle.crypto.CipherParameters;
|
||||
@@ -48,7 +47,6 @@ 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 org.spongycastle.util.Strings;
|
||||
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
|
||||
@@ -114,7 +112,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
public SecretKey generateSecretKey() {
|
||||
byte[] b = new byte[CIPHER_KEY_BYTES];
|
||||
secureRandom.nextBytes(b);
|
||||
return new SecretKeyImpl(b);
|
||||
return new SecretKey(b);
|
||||
}
|
||||
|
||||
public MessageDigest getMessageDigest() {
|
||||
@@ -188,8 +186,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
int[] codes = new int[2];
|
||||
codes[0] = ByteUtils.readUint(alice, CODE_BITS);
|
||||
codes[1] = ByteUtils.readUint(bob, CODE_BITS);
|
||||
ByteUtils.erase(alice);
|
||||
ByteUtils.erase(bob);
|
||||
return codes;
|
||||
}
|
||||
|
||||
@@ -223,9 +219,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
byte[] raw = deriveSharedSecret(ourPriv, theirPub);
|
||||
// Derive the cooked secret from the raw secret using the
|
||||
// concatenation KDF
|
||||
byte[] cooked = concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
|
||||
ByteUtils.erase(raw);
|
||||
return cooked;
|
||||
return concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
@@ -296,8 +290,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
|
||||
byte[] key = counterModeKdf(secret, label, context);
|
||||
return new SecretKeyImpl(key);
|
||||
return new SecretKey(counterModeKdf(secret, label, context));
|
||||
}
|
||||
|
||||
public AuthenticatedCipher getFrameCipher() {
|
||||
@@ -313,21 +306,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
ByteUtils.writeUint32(streamNumber, tag, 0);
|
||||
BlockCipher cipher = new AESLightEngine();
|
||||
assert cipher.getBlockSize() == TAG_LENGTH;
|
||||
KeyParameter k = new KeyParameter(tagKey.getEncoded());
|
||||
KeyParameter k = new KeyParameter(tagKey.getBytes());
|
||||
cipher.init(true, k);
|
||||
cipher.processBlock(tag, 0, tag, 0);
|
||||
ByteUtils.erase(k.getKey());
|
||||
}
|
||||
|
||||
public byte[] encryptWithPassword(byte[] input, char[] password) {
|
||||
public byte[] encryptWithPassword(byte[] input, String password) {
|
||||
// Generate a random salt
|
||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||
secureRandom.nextBytes(salt);
|
||||
// Calibrate the KDF
|
||||
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
|
||||
// Derive the key from the password
|
||||
byte[] keyBytes = pbkdf2(password, salt, iterations);
|
||||
SecretKey key = new SecretKeyImpl(keyBytes);
|
||||
SecretKey key = new SecretKey(pbkdf2(password, salt, iterations));
|
||||
// Generate a random IV
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
secureRandom.nextBytes(iv);
|
||||
@@ -348,12 +339,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return output;
|
||||
} catch(GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
key.erase();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decryptWithPassword(byte[] input, char[] password) {
|
||||
public byte[] decryptWithPassword(byte[] input, String password) {
|
||||
// The input contains the salt, iterations, IV, ciphertext and MAC
|
||||
if(input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + MAC_BYTES)
|
||||
return null; // Invalid
|
||||
@@ -365,8 +354,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||
System.arraycopy(input, salt.length + 4, iv, 0, iv.length);
|
||||
// Derive the key from the password
|
||||
byte[] keyBytes = pbkdf2(password, salt, (int) iterations);
|
||||
SecretKey key = new SecretKeyImpl(keyBytes);
|
||||
SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations));
|
||||
// Initialise the cipher
|
||||
AuthenticatedCipher cipher;
|
||||
try {
|
||||
@@ -374,7 +362,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
cipher = new AuthenticatedCipherImpl(a, MAC_BYTES);
|
||||
cipher.init(false, key, iv, null);
|
||||
} catch(GeneralSecurityException e) {
|
||||
key.erase();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// Try to decrypt the ciphertext (may be invalid)
|
||||
@@ -385,9 +372,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
cipher.doFinal(input, inputOff, inputLen, output, 0);
|
||||
return output;
|
||||
} catch(GeneralSecurityException e) {
|
||||
return null; // Invalid
|
||||
} finally {
|
||||
key.erase();
|
||||
return null; // Invalid ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +402,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// The secret is the first CIPHER_KEY_BYTES bytes of the hash
|
||||
byte[] output = new byte[CIPHER_KEY_BYTES];
|
||||
System.arraycopy(hash, 0, output, 0, output.length);
|
||||
ByteUtils.erase(hash);
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -447,20 +431,17 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
prf.update((byte) CIPHER_KEY_BYTES); // Output length
|
||||
prf.doFinal(mac, 0);
|
||||
System.arraycopy(mac, 0, output, 0, output.length);
|
||||
ByteUtils.erase(mac);
|
||||
ByteUtils.erase(k.getKey());
|
||||
return output;
|
||||
}
|
||||
|
||||
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||
private byte[] pbkdf2(char[] password, byte[] salt, int iterations) {
|
||||
byte[] utf8 = toUtf8ByteArray(password);
|
||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||
byte[] utf8 = StringUtils.toUtf8(password);
|
||||
Digest digest = new SHA384Digest();
|
||||
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
|
||||
gen.init(utf8, salt, iterations);
|
||||
int keyLengthInBits = CIPHER_KEY_BYTES * 8;
|
||||
CipherParameters p = gen.generateDerivedParameters(keyLengthInBits);
|
||||
ByteUtils.erase(utf8);
|
||||
return ((KeyParameter) p).getKey();
|
||||
}
|
||||
|
||||
@@ -512,18 +493,4 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
if(size % 2 == 1) return list.get(size / 2);
|
||||
return list.get(size / 2 - 1) + list.get(size / 2) / 2;
|
||||
}
|
||||
|
||||
private byte[] toUtf8ByteArray(char[] c) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
Strings.toUTF8ByteArray(c, out);
|
||||
byte[] utf8 = out.toByteArray();
|
||||
// Erase the output stream's buffer
|
||||
out.reset();
|
||||
out.write(new byte[utf8.length]);
|
||||
return utf8;
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
||||
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
|
||||
DIGIT + OTHER, 10));
|
||||
|
||||
public float estimateStrength(char[] password) {
|
||||
public float estimateStrength(String password) {
|
||||
HashSet<Character> unique = new HashSet<Character>();
|
||||
for(char c : password) unique.add(c);
|
||||
int length = password.length();
|
||||
for(int i = 0; i < length; i++) unique.add(password.charAt(i));
|
||||
boolean lower = false, upper = false, digit = false, other = false;
|
||||
for(char c : unique) {
|
||||
if(Character.isLowerCase(c)) lower = true;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.briarproject.crypto;
|
||||
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.util.ByteUtils;
|
||||
|
||||
class SecretKeyImpl implements SecretKey {
|
||||
|
||||
private final byte[] key;
|
||||
|
||||
private boolean erased = false; // Locking: this
|
||||
|
||||
SecretKeyImpl(byte[] key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public synchronized byte[] getEncoded() {
|
||||
if(erased) throw new IllegalStateException();
|
||||
return key;
|
||||
}
|
||||
|
||||
public SecretKey copy() {
|
||||
return new SecretKeyImpl(key.clone());
|
||||
}
|
||||
|
||||
public synchronized void erase() {
|
||||
if(erased) throw new IllegalStateException();
|
||||
ByteUtils.erase(key);
|
||||
erased = true;
|
||||
}
|
||||
}
|
||||
@@ -44,16 +44,11 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
||||
if(finalFrame) return -1;
|
||||
// Read the frame
|
||||
int ciphertextLength = 0;
|
||||
try {
|
||||
while(ciphertextLength < frameLength) {
|
||||
int read = in.read(ciphertext, ciphertextLength,
|
||||
frameLength - ciphertextLength);
|
||||
if(read == -1) break; // We'll check the length later
|
||||
ciphertextLength += read;
|
||||
}
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
throw e;
|
||||
while(ciphertextLength < frameLength) {
|
||||
int read = in.read(ciphertext, ciphertextLength,
|
||||
frameLength - ciphertextLength);
|
||||
if(read == -1) break; // We'll check the length later
|
||||
ciphertextLength += read;
|
||||
}
|
||||
int plaintextLength = ciphertextLength - MAC_LENGTH;
|
||||
if(plaintextLength < HEADER_LENGTH) throw new EOFException();
|
||||
|
||||
@@ -30,7 +30,6 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
SecretKey tagKey = crypto.deriveTagKey(secret, alice);
|
||||
crypto.encodeTag(tag, tagKey, streamNumber);
|
||||
tagKey.erase();
|
||||
// Derive the frame key
|
||||
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
|
||||
// Create the encrypter
|
||||
|
||||
@@ -45,12 +45,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
||||
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
|
||||
// Write the tag if required
|
||||
if(writeTag) {
|
||||
try {
|
||||
out.write(tag, 0, tag.length);
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
throw e;
|
||||
}
|
||||
out.write(tag, 0, tag.length);
|
||||
writeTag = false;
|
||||
}
|
||||
// Don't pad the final frame
|
||||
@@ -81,24 +76,14 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
||||
throw new RuntimeException(badCipher);
|
||||
}
|
||||
// Write the frame
|
||||
try {
|
||||
out.write(ciphertext, 0, ciphertextLength);
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
throw e;
|
||||
}
|
||||
out.write(ciphertext, 0, ciphertextLength);
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
// Write the tag if required
|
||||
if(writeTag) {
|
||||
try {
|
||||
out.write(tag, 0, tag.length);
|
||||
} catch(IOException e) {
|
||||
frameKey.erase();
|
||||
throw e;
|
||||
}
|
||||
out.write(tag, 0, tag.length);
|
||||
writeTag = false;
|
||||
}
|
||||
out.flush();
|
||||
|
||||
@@ -81,34 +81,18 @@ class H2Database extends JdbcDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
byte[] key = config.getEncryptionKey();
|
||||
if(key == null) throw new IllegalStateException();
|
||||
char[] password = encodePassword(key);
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", "user");
|
||||
props.put("password", password);
|
||||
try {
|
||||
return DriverManager.getConnection(url, props);
|
||||
} finally {
|
||||
for(int i = 0; i < password.length; i++) password[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private char[] encodePassword(byte[] key) {
|
||||
// The database password is the hex-encoded key
|
||||
char[] hex = StringUtils.toHexChars(key);
|
||||
// Separate the database password from the user password with a space
|
||||
char[] user = "password".toCharArray();
|
||||
char[] combined = new char[hex.length + 1 + user.length];
|
||||
System.arraycopy(hex, 0, combined, 0, hex.length);
|
||||
combined[hex.length] = ' ';
|
||||
System.arraycopy(user, 0, combined, hex.length + 1, user.length);
|
||||
// Erase the hex-encoded key
|
||||
for(int i = 0; i < hex.length; i++) hex[i] = 0;
|
||||
return combined;
|
||||
// Separate the file password from the user password with a space
|
||||
props.put("password", StringUtils.toHexString(key) + " password");
|
||||
return DriverManager.getConnection(url, props);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void flushBuffersToDisk(Statement s) throws SQLException {
|
||||
// FIXME: Remove this after implementing BTPv2?
|
||||
s.execute("CHECKPOINT SYNC");
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.briarproject.api.transport.StreamContext;
|
||||
import org.briarproject.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.api.transport.StreamWriterFactory;
|
||||
import org.briarproject.api.transport.TagRecogniser;
|
||||
import org.briarproject.util.ByteUtils;
|
||||
|
||||
class ConnectionManagerImpl implements ConnectionManager {
|
||||
|
||||
@@ -95,40 +94,28 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
|
||||
private MessagingSession createIncomingSession(StreamContext ctx,
|
||||
TransportConnectionReader r) throws IOException {
|
||||
try {
|
||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||
r.getInputStream(), r.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createIncomingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), streamReader);
|
||||
} finally {
|
||||
ByteUtils.erase(ctx.getSecret());
|
||||
}
|
||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||
r.getInputStream(), r.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createIncomingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), streamReader);
|
||||
}
|
||||
|
||||
private MessagingSession createSimplexOutgoingSession(StreamContext ctx,
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
try {
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), w.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createSimplexOutgoingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
|
||||
streamWriter);
|
||||
} finally {
|
||||
ByteUtils.erase(ctx.getSecret());
|
||||
}
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), w.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createSimplexOutgoingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
|
||||
streamWriter);
|
||||
}
|
||||
|
||||
private MessagingSession createDuplexOutgoingSession(StreamContext ctx,
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
try {
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), w.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createDuplexOutgoingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
|
||||
w.getMaxIdleTime(), streamWriter);
|
||||
} finally {
|
||||
ByteUtils.erase(ctx.getSecret());
|
||||
}
|
||||
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), w.getMaxFrameLength(), ctx);
|
||||
return messagingSessionFactory.createDuplexOutgoingSession(
|
||||
ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(),
|
||||
w.getMaxIdleTime(), streamWriter);
|
||||
}
|
||||
|
||||
private class ManageIncomingSimplexConnection implements Runnable {
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.briarproject.api.transport.Endpoint;
|
||||
import org.briarproject.api.transport.StreamContext;
|
||||
import org.briarproject.api.transport.TagRecogniser;
|
||||
import org.briarproject.api.transport.TemporarySecret;
|
||||
import org.briarproject.util.ByteUtils;
|
||||
|
||||
// FIXME: Don't make alien calls with a lock held
|
||||
class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
@@ -119,7 +118,6 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
Integer maxLatency = maxLatencies.get(s.getTransportId());
|
||||
if(maxLatency == null) {
|
||||
LOG.info("Discarding obsolete secret");
|
||||
ByteUtils.erase(s.getSecret());
|
||||
continue;
|
||||
}
|
||||
long rotation = maxLatency + MAX_CLOCK_DIFFERENCE;
|
||||
@@ -143,7 +141,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
return dead;
|
||||
}
|
||||
|
||||
// Replaces and erases the given secrets and returns any secrets created
|
||||
// Replaces the given secrets and returns any secrets created
|
||||
// Locking: this
|
||||
private Collection<TemporarySecret> replaceDeadSecrets(long now,
|
||||
Collection<TemporarySecret> dead) {
|
||||
@@ -157,12 +155,10 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
// There's no other secret for this endpoint
|
||||
newest.put(k, s);
|
||||
} else if(exists.getPeriod() < s.getPeriod()) {
|
||||
// There's an older secret - erase it and use this one instead
|
||||
ByteUtils.erase(exists.getSecret());
|
||||
// There's an older secret - use this one instead
|
||||
newest.put(k, s);
|
||||
} else {
|
||||
// There's a newer secret - erase this one
|
||||
ByteUtils.erase(s.getSecret());
|
||||
// There's a newer secret - keep using it
|
||||
}
|
||||
}
|
||||
Collection<TemporarySecret> created = new ArrayList<TemporarySecret>();
|
||||
@@ -179,34 +175,23 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
throw new IllegalStateException();
|
||||
// Derive the old, current and new secrets
|
||||
byte[] b1 = s.getSecret();
|
||||
for(long p = s.getPeriod() + 1; p < period; p++) {
|
||||
byte[] temp = crypto.deriveNextSecret(b1, p);
|
||||
ByteUtils.erase(b1);
|
||||
b1 = temp;
|
||||
}
|
||||
for(long p = s.getPeriod() + 1; p < period; p++)
|
||||
b1 = crypto.deriveNextSecret(b1, p);
|
||||
byte[] b2 = crypto.deriveNextSecret(b1, period);
|
||||
byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
|
||||
// Add the secrets to their respective maps - copies may already
|
||||
// exist, in which case erase the new copies (the old copies are
|
||||
// referenced by the connection recogniser)
|
||||
// Add the secrets to their respective maps if not already present
|
||||
EndpointKey k = e.getKey();
|
||||
if(oldSecrets.containsKey(k)) {
|
||||
ByteUtils.erase(b1);
|
||||
} else {
|
||||
if(!oldSecrets.containsKey(k)) {
|
||||
TemporarySecret s1 = new TemporarySecret(s, period - 1, b1);
|
||||
oldSecrets.put(k, s1);
|
||||
created.add(s1);
|
||||
}
|
||||
if(currentSecrets.containsKey(k)) {
|
||||
ByteUtils.erase(b2);
|
||||
} else {
|
||||
if(!currentSecrets.containsKey(k)) {
|
||||
TemporarySecret s2 = new TemporarySecret(s, period, b2);
|
||||
currentSecrets.put(k, s2);
|
||||
created.add(s2);
|
||||
}
|
||||
if(newSecrets.containsKey(k)) {
|
||||
ByteUtils.erase(b3);
|
||||
} else {
|
||||
if(!newSecrets.containsKey(k)) {
|
||||
TemporarySecret s3 = new TemporarySecret(s, period + 1, b3);
|
||||
newSecrets.put(k, s3);
|
||||
created.add(s3);
|
||||
@@ -220,18 +205,12 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
timer.cancel();
|
||||
tagRecogniser.removeSecrets();
|
||||
maxLatencies.clear();
|
||||
removeAndEraseSecrets(oldSecrets);
|
||||
removeAndEraseSecrets(currentSecrets);
|
||||
removeAndEraseSecrets(newSecrets);
|
||||
oldSecrets.clear();
|
||||
currentSecrets.clear();
|
||||
newSecrets.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Locking: this
|
||||
private void removeAndEraseSecrets(Map<?, TemporarySecret> m) {
|
||||
for(TemporarySecret s : m.values()) ByteUtils.erase(s.getSecret());
|
||||
m.clear();
|
||||
}
|
||||
|
||||
public synchronized StreamContext getStreamContext(ContactId c,
|
||||
TransportId t) {
|
||||
TemporarySecret s = currentSecrets.get(new EndpointKey(c, t));
|
||||
@@ -250,8 +229,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
// Clone the secret - the original will be erased
|
||||
byte[] secret = s.getSecret().clone();
|
||||
byte[] secret = s.getSecret();
|
||||
return new StreamContext(c, t, secret, streamNumber, s.getAlice());
|
||||
}
|
||||
|
||||
@@ -265,11 +243,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
if(period < 1) throw new IllegalStateException();
|
||||
// Derive the old, current and new secrets
|
||||
byte[] b1 = initialSecret;
|
||||
for(long p = 0; p < period; p++) {
|
||||
byte[] temp = crypto.deriveNextSecret(b1, p);
|
||||
ByteUtils.erase(b1);
|
||||
b1 = temp;
|
||||
}
|
||||
for(long p = 0; p < period; p++)
|
||||
b1 = crypto.deriveNextSecret(b1, p);
|
||||
byte[] b2 = crypto.deriveNextSecret(b1, period);
|
||||
byte[] b3 = crypto.deriveNextSecret(b2, period + 1);
|
||||
TemporarySecret s1 = new TemporarySecret(ep, period - 1, b1);
|
||||
@@ -341,28 +316,17 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
}
|
||||
|
||||
// Locking: this
|
||||
private void removeAndEraseSecrets(ContactId c, Map<?, TemporarySecret> m) {
|
||||
private void removeSecrets(ContactId c, Map<?, TemporarySecret> m) {
|
||||
Iterator<TemporarySecret> it = m.values().iterator();
|
||||
while(it.hasNext()) {
|
||||
TemporarySecret s = it.next();
|
||||
if(s.getContactId().equals(c)) {
|
||||
ByteUtils.erase(s.getSecret());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
while(it.hasNext())
|
||||
if(it.next().getContactId().equals(c)) it.remove();
|
||||
}
|
||||
|
||||
// Locking: this
|
||||
private void removeAndEraseSecrets(TransportId t,
|
||||
Map<?, TemporarySecret> m) {
|
||||
private void removeSecrets(TransportId t, Map<?, TemporarySecret> m) {
|
||||
Iterator<TemporarySecret> it = m.values().iterator();
|
||||
while(it.hasNext()) {
|
||||
TemporarySecret s = it.next();
|
||||
if(s.getTransportId().equals(t)) {
|
||||
ByteUtils.erase(s.getSecret());
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
while(it.hasNext())
|
||||
if(it.next().getTransportId().equals(t)) it.remove();
|
||||
}
|
||||
|
||||
private static class EndpointKey {
|
||||
@@ -408,9 +372,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
ContactId c = event.getContactId();
|
||||
tagRecogniser.removeSecrets(c);
|
||||
synchronized(KeyManagerImpl.this) {
|
||||
removeAndEraseSecrets(c, oldSecrets);
|
||||
removeAndEraseSecrets(c, currentSecrets);
|
||||
removeAndEraseSecrets(c, newSecrets);
|
||||
removeSecrets(c, oldSecrets);
|
||||
removeSecrets(c, currentSecrets);
|
||||
removeSecrets(c, newSecrets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,9 +409,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, EventListener {
|
||||
tagRecogniser.removeSecrets(t);
|
||||
synchronized(KeyManagerImpl.this) {
|
||||
maxLatencies.remove(t);
|
||||
removeAndEraseSecrets(t, oldSecrets);
|
||||
removeAndEraseSecrets(t, currentSecrets);
|
||||
removeAndEraseSecrets(t, newSecrets);
|
||||
removeSecrets(t, oldSecrets);
|
||||
removeSecrets(t, currentSecrets);
|
||||
removeSecrets(t, newSecrets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +56,10 @@ class TransportTagRecogniser {
|
||||
assert duplicate == null;
|
||||
}
|
||||
}
|
||||
key.erase();
|
||||
// Store the updated reordering window in the DB
|
||||
db.setReorderingWindow(t.contactId, transportId, t.period,
|
||||
t.window.getCentre(), t.window.getBitmap());
|
||||
// Clone the secret - the key manager will erase the original
|
||||
byte[] secret = t.secret.clone();
|
||||
return new StreamContext(t.contactId, transportId, secret,
|
||||
return new StreamContext(t.contactId, transportId, t.secret,
|
||||
t.streamNumber, t.alice);
|
||||
}
|
||||
|
||||
@@ -84,7 +81,6 @@ class TransportTagRecogniser {
|
||||
TagContext duplicate = tagMap.put(new Bytes(tag), added);
|
||||
assert duplicate == null;
|
||||
}
|
||||
key.erase();
|
||||
// Create a removal context to remove the window and the tags later
|
||||
RemovalContext r = new RemovalContext(window, secret, alice);
|
||||
removalMap.put(new RemovalKey(contactId, period), r);
|
||||
@@ -107,7 +103,6 @@ class TransportTagRecogniser {
|
||||
TagContext removed = tagMap.remove(new Bytes(tag));
|
||||
assert removed != null;
|
||||
}
|
||||
key.erase();
|
||||
}
|
||||
|
||||
synchronized void removeSecrets(ContactId c) {
|
||||
|
||||
@@ -48,10 +48,6 @@ public class ByteUtils {
|
||||
| ((b[offset + 2] & 0xFFL) << 8) | (b[offset + 3] & 0xFFL);
|
||||
}
|
||||
|
||||
public static void erase(byte[] b) {
|
||||
for(int i = 0; i < b.length; i++) b[i] = 0;
|
||||
}
|
||||
|
||||
public static int readUint(byte[] b, int bits) {
|
||||
if(b.length << 3 < bits) throw new IllegalArgumentException();
|
||||
int result = 0;
|
||||
|
||||
Reference in New Issue
Block a user