mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Erase keys after using them. (Copies created by ciphers, etc, may
still exist.)
This commit is contained in:
@@ -4,6 +4,9 @@ import javax.crypto.SecretKey;
|
|||||||
|
|
||||||
public interface ErasableKey extends SecretKey {
|
public interface ErasableKey extends SecretKey {
|
||||||
|
|
||||||
|
/** Returns a copy of the key. */
|
||||||
|
ErasableKey copy();
|
||||||
|
|
||||||
/** Erases the key from memory. */
|
/** Erases the key from memory. */
|
||||||
void erase();
|
void erase();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class ErasableKeyImpl implements ErasableKey {
|
|||||||
return "RAW";
|
return "RAW";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ErasableKey copy() {
|
||||||
|
return new ErasableKeyImpl(getEncoded(), algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
public void erase() {
|
public void erase() {
|
||||||
if(erased) throw new IllegalStateException();
|
if(erased) throw new IllegalStateException();
|
||||||
ByteUtils.erase(key);
|
ByteUtils.erase(key);
|
||||||
|
|||||||
@@ -43,45 +43,58 @@ implements ConnectionDecrypter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void readMac(byte[] mac) throws IOException {
|
public void readMac(byte[] mac) throws IOException {
|
||||||
if(betweenFrames) throw new IllegalStateException();
|
|
||||||
// If we have any plaintext in the buffer, copy it into the MAC
|
|
||||||
System.arraycopy(buf, bufOff, mac, 0, bufLen);
|
|
||||||
// Read the remainder of the MAC
|
|
||||||
int offset = bufLen;
|
|
||||||
while(offset < mac.length) {
|
|
||||||
int read = in.read(mac, offset, mac.length - offset);
|
|
||||||
if(read == -1) break;
|
|
||||||
offset += read;
|
|
||||||
}
|
|
||||||
if(offset < mac.length) throw new EOFException(); // Unexpected EOF
|
|
||||||
// Decrypt the remainder of the MAC
|
|
||||||
try {
|
try {
|
||||||
int length = mac.length - bufLen;
|
if(betweenFrames) throw new IllegalStateException();
|
||||||
int i = frameCipher.doFinal(mac, bufLen, length, mac, bufLen);
|
// If we have any plaintext in the buffer, copy it into the MAC
|
||||||
if(i < length) throw new RuntimeException();
|
System.arraycopy(buf, bufOff, mac, 0, bufLen);
|
||||||
} catch(BadPaddingException badCipher) {
|
// Read the remainder of the MAC
|
||||||
throw new RuntimeException(badCipher);
|
int offset = bufLen;
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
while(offset < mac.length) {
|
||||||
throw new RuntimeException(badCipher);
|
int read = in.read(mac, offset, mac.length - offset);
|
||||||
} catch(ShortBufferException badCipher) {
|
if(read == -1) break;
|
||||||
throw new RuntimeException(badCipher);
|
offset += read;
|
||||||
|
}
|
||||||
|
if(offset < mac.length) throw new EOFException(); // Unexpected EOF
|
||||||
|
// Decrypt the remainder of the MAC
|
||||||
|
try {
|
||||||
|
int length = mac.length - bufLen;
|
||||||
|
int i = frameCipher.doFinal(mac, bufLen, length, mac, bufLen);
|
||||||
|
if(i < length) throw new RuntimeException();
|
||||||
|
} catch(BadPaddingException badCipher) {
|
||||||
|
throw new RuntimeException(badCipher);
|
||||||
|
} catch(IllegalBlockSizeException badCipher) {
|
||||||
|
throw new RuntimeException(badCipher);
|
||||||
|
} catch(ShortBufferException badCipher) {
|
||||||
|
throw new RuntimeException(badCipher);
|
||||||
|
}
|
||||||
|
bufOff = bufLen = 0;
|
||||||
|
betweenFrames = true;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
bufOff = bufLen = 0;
|
|
||||||
betweenFrames = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
if(betweenFrames) initialiseCipher();
|
try {
|
||||||
if(bufLen == 0) {
|
if(betweenFrames) initialiseCipher();
|
||||||
if(!readBlock()) return -1;
|
if(bufLen == 0) {
|
||||||
bufOff = 0;
|
if(!readBlock()) {
|
||||||
bufLen = buf.length;
|
frameKey.erase();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bufOff = 0;
|
||||||
|
bufLen = buf.length;
|
||||||
|
}
|
||||||
|
int i = buf[bufOff];
|
||||||
|
bufOff++;
|
||||||
|
bufLen--;
|
||||||
|
return i < 0 ? i + 256 : i;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
int i = buf[bufOff];
|
|
||||||
bufOff++;
|
|
||||||
bufLen--;
|
|
||||||
return i < 0 ? i + 256 : i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -91,17 +104,25 @@ implements ConnectionDecrypter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
if(betweenFrames) initialiseCipher();
|
try {
|
||||||
if(bufLen == 0) {
|
if(betweenFrames) initialiseCipher();
|
||||||
if(!readBlock()) return -1;
|
if(bufLen == 0) {
|
||||||
bufOff = 0;
|
if(!readBlock()) {
|
||||||
bufLen = buf.length;
|
frameKey.erase();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bufOff = 0;
|
||||||
|
bufLen = buf.length;
|
||||||
|
}
|
||||||
|
int length = Math.min(len, bufLen);
|
||||||
|
System.arraycopy(buf, bufOff, b, off, length);
|
||||||
|
bufOff += length;
|
||||||
|
bufLen -= length;
|
||||||
|
return length;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
int length = Math.min(len, bufLen);
|
|
||||||
System.arraycopy(buf, bufOff, b, off, length);
|
|
||||||
bufOff += length;
|
|
||||||
bufLen -= length;
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Although we're using CTR mode, which doesn't require full blocks of
|
// Although we're using CTR mode, which doesn't require full blocks of
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ implements ConnectionEncrypter {
|
|||||||
}
|
}
|
||||||
if(encryptedIv.length != IV_LENGTH)
|
if(encryptedIv.length != IV_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
ivKey.erase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
@@ -53,16 +54,21 @@ implements ConnectionEncrypter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeMac(byte[] mac) throws IOException {
|
public void writeMac(byte[] mac) throws IOException {
|
||||||
if(!ivWritten || betweenFrames) throw new IllegalStateException();
|
|
||||||
try {
|
try {
|
||||||
out.write(frameCipher.doFinal(mac));
|
if(!ivWritten || betweenFrames) throw new IllegalStateException();
|
||||||
} catch(BadPaddingException badCipher) {
|
try {
|
||||||
throw new RuntimeException(badCipher);
|
out.write(frameCipher.doFinal(mac));
|
||||||
} catch(IllegalBlockSizeException badCipher) {
|
} catch(BadPaddingException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
|
} catch(IllegalBlockSizeException badCipher) {
|
||||||
|
throw new RuntimeException(badCipher);
|
||||||
|
}
|
||||||
|
capacity -= mac.length;
|
||||||
|
betweenFrames = true;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
capacity -= mac.length;
|
|
||||||
betweenFrames = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRemainingCapacity() {
|
public long getRemainingCapacity() {
|
||||||
@@ -71,11 +77,16 @@ implements ConnectionEncrypter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
if(!ivWritten) writeIv();
|
try {
|
||||||
if(betweenFrames) initialiseCipher();
|
if(!ivWritten) writeIv();
|
||||||
byte[] ciphertext = frameCipher.update(new byte[] {(byte) b});
|
if(betweenFrames) initialiseCipher();
|
||||||
if(ciphertext != null) out.write(ciphertext);
|
byte[] ciphertext = frameCipher.update(new byte[] {(byte) b});
|
||||||
capacity--;
|
if(ciphertext != null) out.write(ciphertext);
|
||||||
|
capacity--;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,11 +96,16 @@ implements ConnectionEncrypter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
if(!ivWritten) writeIv();
|
try {
|
||||||
if(betweenFrames) initialiseCipher();
|
if(!ivWritten) writeIv();
|
||||||
byte[] ciphertext = frameCipher.update(b, off, len);
|
if(betweenFrames) initialiseCipher();
|
||||||
if(ciphertext != null) out.write(ciphertext);
|
byte[] ciphertext = frameCipher.update(b, off, len);
|
||||||
capacity -= len;
|
if(ciphertext != null) out.write(ciphertext);
|
||||||
|
capacity -= len;
|
||||||
|
} catch(IOException e) {
|
||||||
|
frameKey.erase();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeIv() throws IOException {
|
private void writeIv() throws IOException {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
|
|||||||
} catch(InvalidKeyException badKey) {
|
} catch(InvalidKeyException badKey) {
|
||||||
throw new IllegalArgumentException(badKey);
|
throw new IllegalArgumentException(badKey);
|
||||||
}
|
}
|
||||||
|
ivKey.erase();
|
||||||
// Validate the IV
|
// Validate the IV
|
||||||
if(!IvEncoder.validateIv(iv, true, ctx))
|
if(!IvEncoder.validateIv(iv, true, ctx))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ implements ConnectionReader {
|
|||||||
} catch(InvalidKeyException e) {
|
} catch(InvalidKeyException e) {
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
macKey.erase();
|
||||||
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
||||||
header = new byte[4];
|
header = new byte[4];
|
||||||
payload = new byte[maxPayloadLength];
|
payload = new byte[maxPayloadLength];
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ DatabaseListener {
|
|||||||
byte[] secret = e.getValue();
|
byte[] secret = e.getValue();
|
||||||
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
ErasableKey ivKey = crypto.deriveIvKey(secret, true);
|
||||||
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
|
Bytes iv = new Bytes(encryptIv(i, unseen, ivKey));
|
||||||
|
ivKey.erase();
|
||||||
expected.put(iv, new ConnectionContextImpl(c, i, unseen, secret));
|
expected.put(iv, new ConnectionContextImpl(c, i, unseen, secret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
|
|||||||
} catch(InvalidKeyException badKey) {
|
} catch(InvalidKeyException badKey) {
|
||||||
throw new RuntimeException(badKey);
|
throw new RuntimeException(badKey);
|
||||||
}
|
}
|
||||||
|
ivKey.erase();
|
||||||
// Validate the IV
|
// Validate the IV
|
||||||
if(!IvEncoder.validateIv(iv, true, ctx))
|
if(!IvEncoder.validateIv(iv, true, ctx))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ implements ConnectionWriter {
|
|||||||
} catch(InvalidKeyException badKey) {
|
} catch(InvalidKeyException badKey) {
|
||||||
throw new IllegalArgumentException(badKey);
|
throw new IllegalArgumentException(badKey);
|
||||||
}
|
}
|
||||||
|
macKey.erase();
|
||||||
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
maxPayloadLength = MAX_FRAME_LENGTH - 4 - mac.getMacLength();
|
||||||
buf = new ByteArrayOutputStream(maxPayloadLength);
|
buf = new ByteArrayOutputStream(maxPayloadLength);
|
||||||
header = new byte[4];
|
header = new byte[4];
|
||||||
|
|||||||
@@ -74,12 +74,16 @@ public class FrameReadWriteTest extends TestCase {
|
|||||||
random.nextBytes(frame);
|
random.nextBytes(frame);
|
||||||
byte[] frame1 = new byte[321];
|
byte[] frame1 = new byte[321];
|
||||||
random.nextBytes(frame1);
|
random.nextBytes(frame1);
|
||||||
|
// Copy the keys - the copies will be erased
|
||||||
|
ErasableKey frameCopy = frameKey.copy();
|
||||||
|
ErasableKey ivCopy = ivKey.copy();
|
||||||
|
ErasableKey macCopy = macKey.copy();
|
||||||
// Write the frames
|
// Write the frames
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
|
||||||
Long.MAX_VALUE, iv, ivCipher, frameCipher, ivKey, frameKey);
|
Long.MAX_VALUE, iv, ivCipher, frameCipher, ivCopy, frameCopy);
|
||||||
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac,
|
ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac,
|
||||||
macKey);
|
macCopy);
|
||||||
OutputStream out1 = writer.getOutputStream();
|
OutputStream out1 = writer.getOutputStream();
|
||||||
out1.write(frame);
|
out1.write(frame);
|
||||||
out1.flush();
|
out1.flush();
|
||||||
|
|||||||
Reference in New Issue
Block a user