Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
best effort clearing of unlock-related memory
Browse files Browse the repository at this point in the history
Java doesn't provide any decent way to deal with this, since data could
have been copied all over the stack/heap and there's no way to guarantee
that zeroing happens. However, we do control the implementation of the
Java runtime so we can figure out what it does in practice and we can
make changes to the implementation to help with this.

TODO:

* Destroyable needs to be implemented for each SecretKeySpec implementation
* much more needs to be zeroed
* Android Runtime heap zeroing
  • Loading branch information
thestinger committed Feb 2, 2024
1 parent 3e773e4 commit 770ff63
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.DestroyFailedException;

/**
* Implementation of NIST SP800-108
Expand All @@ -43,7 +44,13 @@ class SP800Derive {
private Mac getMac() {
try {
final Mac m = Mac.getInstance("HmacSHA256");
m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm()));
SecretKeySpec key = new SecretKeySpec(mKeyBytes, m.getAlgorithm());
m.init(key);
try {
key.destroy();
} catch (DestroyFailedException e) {
Slog.e(TAG, "Failed to destroy key", e);
}
return m;
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
Expand All @@ -61,7 +68,11 @@ public byte[] fixedInput(byte[] fixedInput) {
final Mac m = getMac();
update32(m, 1); // Hardwired counter value
m.update(fixedInput);
return m.doFinal();
byte[] result = m.doFinal();
try {
m.init(new SecretKeySpec(new byte[32], m.getAlgorithm()));
} catch (InvalidKeyException e) {}
return result;
}

/**
Expand All @@ -77,6 +88,10 @@ public byte[] withContext(byte[] label, byte[] context) {
m.update(context);
update32(m, context.length * 8); // Disambiguate context
update32(m, 256); // Hardwired output length
return m.doFinal();
byte[] result = m.doFinal();
try {
m.init(new SecretKeySpec(new byte[32], m.getAlgorithm()));
} catch (InvalidKeyException e) {}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.DestroyFailedException;

class SyntheticPasswordCrypto {
private static final String TAG = "SyntheticPasswordCrypto";
Expand Down Expand Up @@ -100,10 +101,19 @@ private static byte[] encrypt(SecretKey key, byte[] blob)

public static byte[] encrypt(byte[] keyBytes, byte[] personalization, byte[] message) {
byte[] keyHash = personalizedHash(personalization, keyBytes);
SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(keyHash, AES_GCM_KEY_SIZE),
byte[] aesKey = Arrays.copyOf(keyHash, AES_GCM_KEY_SIZE);
SecretKeySpec key = new SecretKeySpec(aesKey,
KeyProperties.KEY_ALGORITHM_AES);
Arrays.fill(aesKey, (byte) 0);
Arrays.fill(keyHash, (byte) 0);
try {
return encrypt(key, message);
byte[] result = encrypt(key, message);
try {
key.destroy();
} catch (DestroyFailedException e) {
Slog.e(TAG, "Failed to destroy key", e);
}
return result;
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException | IOException
| InvalidParameterSpecException e) {
Expand All @@ -114,10 +124,19 @@ public static byte[] encrypt(byte[] keyBytes, byte[] personalization, byte[] mes

public static byte[] decrypt(byte[] keyBytes, byte[] personalization, byte[] ciphertext) {
byte[] keyHash = personalizedHash(personalization, keyBytes);
SecretKeySpec key = new SecretKeySpec(Arrays.copyOf(keyHash, AES_GCM_KEY_SIZE),
byte[] aesKey = Arrays.copyOf(keyHash, AES_GCM_KEY_SIZE);
SecretKeySpec key = new SecretKeySpec(aesKey,
KeyProperties.KEY_ALGORITHM_AES);
Arrays.fill(aesKey, (byte) 0);
Arrays.fill(keyHash, (byte) 0);
try {
return decrypt(key, ciphertext);
byte[] result = decrypt(key, ciphertext);
try {
key.destroy();
} catch (DestroyFailedException e) {
Slog.e(TAG, "Failed to destroy key", e);
}
return result;
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException
| InvalidAlgorithmParameterException e) {
Expand Down Expand Up @@ -265,7 +284,9 @@ protected static byte[] personalizedHash(byte[] personalization, byte[]... messa
for (byte[] data : message) {
digest.update(data);
}
return digest.digest();
byte[] result = digest.digest();
digest.reset();
return result;
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e);
}
Expand Down

0 comments on commit 770ff63

Please sign in to comment.