diff options
| author | 2017-12-18 14:50:23 +0000 | |
|---|---|---|
| committer | 2017-12-20 15:50:46 +0000 | |
| commit | cbd4b2f46fda2cd2ea81b60e341ea64dab1d8b9d (patch) | |
| tree | eb2de090b88dc7f6569b33355004f0cf4b19331f | |
| parent | 13eec4697c376a45531b4c0638f313281407f9b7 (diff) | |
Add tests for KeySyncUtils now that SecureBox is available
SecureBox is to be merged before this.
Test: adb shell am instrument -w -e package com.android.server.locksettings.recoverablekeystore com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Change-Id: I40f783c165faa595c8913ff073915726ae71524d
2 files changed, 215 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java index 37aeb3af051e..e2ba8aa4b1bc 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java @@ -112,7 +112,8 @@ public class KeySyncUtils { * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable. * @throws InvalidKeyException if the hash cannot be used to encrypt for some reason. */ - private static byte[] locallyEncryptRecoveryKey(byte[] lockScreenHash, SecretKey recoveryKey) + @VisibleForTesting + static byte[] locallyEncryptRecoveryKey(byte[] lockScreenHash, SecretKey recoveryKey) throws NoSuchAlgorithmException, InvalidKeyException { return SecureBox.encrypt( /*theirPublicKey=*/ null, diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java index ac3abedba7f3..c328dda68389 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java @@ -16,6 +16,8 @@ package com.android.server.locksettings.recoverablekeystore; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -23,13 +25,20 @@ import static org.junit.Assert.assertFalse; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import com.google.common.collect.ImmutableMap; + import org.junit.Test; import org.junit.runner.RunWith; import java.nio.charset.StandardCharsets; +import java.security.KeyPair; import java.security.MessageDigest; import java.util.Arrays; +import java.util.Map; +import java.util.Random; +import javax.crypto.AEADBadTagException; +import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @SmallTest @@ -39,6 +48,13 @@ public class KeySyncUtilsTest { private static final int THM_KF_HASH_SIZE = 256; private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; private static final String SHA_256_ALGORITHM = "SHA-256"; + private static final String APPLICATION_KEY_ALGORITHM = "AES"; + private static final byte[] LOCK_SCREEN_HASH_1 = + utf8Bytes("g09TEvo6XqVdNaYdRggzn5w2C5rCeE1F"); + private static final byte[] LOCK_SCREEN_HASH_2 = + utf8Bytes("snQzsbvclkSsG6PwasAp1oFLzbq3KtFe"); + private static final byte[] RECOVERY_CLAIM_HEADER = + "V1 KF_claim".getBytes(StandardCharsets.UTF_8); @Test public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception { @@ -97,6 +113,197 @@ public class KeySyncUtilsTest { utf8Bytes("!"))); } + @Test + public void decryptApplicationKey_decryptsAnApplicationKeyEncryptedWithSecureBox() + throws Exception { + String alias = "phoebe"; + SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); + SecretKey applicationKey = generateApplicationKey(); + Map<String, byte[]> encryptedKeys = + KeySyncUtils.encryptKeysWithRecoveryKey( + recoveryKey, ImmutableMap.of(alias, applicationKey)); + byte[] encryptedKey = encryptedKeys.get(alias); + + byte[] keyMaterial = + KeySyncUtils.decryptApplicationKey(recoveryKey.getEncoded(), encryptedKey); + + assertArrayEquals(applicationKey.getEncoded(), keyMaterial); + } + + @Test + public void decryptApplicationKey_throwsIfUnableToDecrypt() throws Exception { + String alias = "casper"; + Map<String, byte[]> encryptedKeys = + KeySyncUtils.encryptKeysWithRecoveryKey( + KeySyncUtils.generateRecoveryKey(), + ImmutableMap.of("casper", generateApplicationKey())); + byte[] encryptedKey = encryptedKeys.get(alias); + + try { + KeySyncUtils.decryptApplicationKey( + KeySyncUtils.generateRecoveryKey().getEncoded(), encryptedKey); + fail("Did not throw decrypting with bad key."); + } catch (AEADBadTagException error) { + // expected + } + } + + @Test + public void decryptRecoveryKey_decryptsALocallyEncryptedKey() throws Exception { + SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); + byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey( + LOCK_SCREEN_HASH_1, recoveryKey); + + byte[] keyMaterial = KeySyncUtils.decryptRecoveryKey(LOCK_SCREEN_HASH_1, encrypted); + + assertArrayEquals(recoveryKey.getEncoded(), keyMaterial); + } + + @Test + public void decryptRecoveryKey_throwsIfCannotDecrypt() throws Exception { + SecretKey recoveryKey = KeySyncUtils.generateRecoveryKey(); + byte[] encrypted = KeySyncUtils.locallyEncryptRecoveryKey(LOCK_SCREEN_HASH_1, recoveryKey); + + try { + KeySyncUtils.decryptRecoveryKey(LOCK_SCREEN_HASH_2, encrypted); + fail("Did not throw decrypting with bad key."); + } catch (AEADBadTagException error) { + // expected + } + } + + @Test + public void encryptRecoveryClaim_encryptsLockScreenAndKeyClaimant() throws Exception { + KeyPair keyPair = SecureBox.genKeyPair(); + byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); + byte[] challenge = randomBytes(32); + byte[] vaultParams = randomBytes(100); + + byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim( + keyPair.getPublic(), + vaultParams, + challenge, + LOCK_SCREEN_HASH_1, + keyClaimant); + + byte[] decrypted = SecureBox.decrypt( + keyPair.getPrivate(), + /*sharedSecret=*/ null, + /*header=*/ KeySyncUtils.concat(RECOVERY_CLAIM_HEADER, vaultParams, challenge), + encryptedRecoveryClaim); + assertArrayEquals(KeySyncUtils.concat(LOCK_SCREEN_HASH_1, keyClaimant), decrypted); + } + + @Test + public void encryptRecoveryClaim_cannotBeDecryptedWithoutChallenge() throws Exception { + KeyPair keyPair = SecureBox.genKeyPair(); + byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); + byte[] vaultParams = randomBytes(100); + + byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim( + keyPair.getPublic(), + vaultParams, + /*challenge=*/ randomBytes(32), + LOCK_SCREEN_HASH_1, + keyClaimant); + + try { + SecureBox.decrypt( + keyPair.getPrivate(), + /*sharedSecret=*/ null, + /*header=*/ KeySyncUtils.concat( + RECOVERY_CLAIM_HEADER, vaultParams, randomBytes(32)), + encryptedRecoveryClaim); + fail("Should throw if challenge is incorrect."); + } catch (AEADBadTagException e) { + // expected + } + } + + @Test + public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectSecretKey() throws Exception { + byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); + byte[] challenge = randomBytes(32); + byte[] vaultParams = randomBytes(100); + + byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim( + SecureBox.genKeyPair().getPublic(), + vaultParams, + challenge, + LOCK_SCREEN_HASH_1, + keyClaimant); + + try { + SecureBox.decrypt( + SecureBox.genKeyPair().getPrivate(), + /*sharedSecret=*/ null, + /*header=*/ KeySyncUtils.concat( + RECOVERY_CLAIM_HEADER, vaultParams, challenge), + encryptedRecoveryClaim); + fail("Should throw if secret key is incorrect."); + } catch (AEADBadTagException e) { + // expected + } + } + + @Test + public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectVaultParams() throws Exception { + KeyPair keyPair = SecureBox.genKeyPair(); + byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); + byte[] challenge = randomBytes(32); + + byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim( + keyPair.getPublic(), + /*vaultParams=*/ randomBytes(100), + challenge, + LOCK_SCREEN_HASH_1, + keyClaimant); + + try { + SecureBox.decrypt( + keyPair.getPrivate(), + /*sharedSecret=*/ null, + /*header=*/ KeySyncUtils.concat( + RECOVERY_CLAIM_HEADER, randomBytes(100), challenge), + encryptedRecoveryClaim); + fail("Should throw if vault params is incorrect."); + } catch (AEADBadTagException e) { + // expected + } + } + + @Test + public void encryptRecoveryClaim_cannotBeDecryptedWithoutCorrectHeader() throws Exception { + KeyPair keyPair = SecureBox.genKeyPair(); + byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); + byte[] challenge = randomBytes(32); + byte[] vaultParams = randomBytes(100); + + byte[] encryptedRecoveryClaim = KeySyncUtils.encryptRecoveryClaim( + keyPair.getPublic(), + vaultParams, + challenge, + LOCK_SCREEN_HASH_1, + keyClaimant); + + try { + SecureBox.decrypt( + keyPair.getPrivate(), + /*sharedSecret=*/ null, + /*header=*/ KeySyncUtils.concat(randomBytes(10), vaultParams, challenge), + encryptedRecoveryClaim); + fail("Should throw if header is incorrect."); + } catch (AEADBadTagException e) { + // expected + } + } + + private static byte[] randomBytes(int n) { + byte[] bytes = new byte[n]; + new Random().nextBytes(bytes); + return bytes; + } + private static byte[] utf8Bytes(String s) { return s.getBytes(StandardCharsets.UTF_8); } @@ -106,4 +313,10 @@ public class KeySyncUtilsTest { messageDigest.update(bytes); return messageDigest.digest(); } + + private static SecretKey generateApplicationKey() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance(APPLICATION_KEY_ALGORITHM); + keyGenerator.init(/*keySize=*/ 256); + return keyGenerator.generateKey(); + } } |