Erase keys after using them. (Copies created by ciphers, etc, may

still exist.)
This commit is contained in:
akwizgran
2011-11-16 16:22:35 +00:00
parent ece03038f4
commit f10512d787
10 changed files with 115 additions and 62 deletions

View File

@@ -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();
} }

View File

@@ -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);

View File

@@ -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

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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];

View File

@@ -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));
} }
} }

View File

@@ -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();

View File

@@ -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];

View File

@@ -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();