Merge "Check the public key bytes of veriferPublicKey matche the ones in vaultParams"

This commit is contained in:
TreeHugger Robot
2018-01-06 18:41:05 +00:00
committed by Android (Google) Code Review
2 changed files with 68 additions and 6 deletions

View File

@@ -58,6 +58,7 @@ import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -323,6 +324,23 @@ public class RecoverableKeyStoreManager {
"Only a single KeyStoreRecoveryMetadata is supported");
}
PublicKey publicKey;
try {
publicKey = KeySyncUtils.deserializePublicKey(verifierPublicKey);
} catch (NoSuchAlgorithmException e) {
// Should never happen
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 key");
}
// The raw public key bytes contained in vaultParams must match the ones given in
// verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned
// by the original recovery service.
if (!publicKeysMatch(publicKey, vaultParams)) {
throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE,
"The public keys given in verifierPublicKey and vaultParams do not match.");
}
byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
byte[] kfHash = secrets.get(0).getSecret();
mRecoverySessionStorage.add(
@@ -331,7 +349,6 @@ public class RecoverableKeyStoreManager {
try {
byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(kfHash);
PublicKey publicKey = KeySyncUtils.deserializePublicKey(verifierPublicKey);
return KeySyncUtils.encryptRecoveryClaim(
publicKey,
vaultParams,
@@ -341,9 +358,8 @@ public class RecoverableKeyStoreManager {
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e);
throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
} catch (InvalidKeySpecException | InvalidKeyException e) {
throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE,
"Not a valid X509 key");
} catch (InvalidKeyException e) {
throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, e.getMessage());
}
}
@@ -352,7 +368,7 @@ public class RecoverableKeyStoreManager {
* service.
*
* @param sessionId The session ID used to generate the claim. See
* {@link #startRecoverySession(String, byte[], byte[], byte[], List, int)}.
* {@link #startRecoverySession(String, byte[], byte[], byte[], List)}.
* @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault
* service.
* @param applicationKeys The encrypted key blobs returned by the remote vault service. These
@@ -509,4 +525,9 @@ public class RecoverableKeyStoreManager {
RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE,
"Caller " + Binder.getCallingUid() + " doesn't have RecoverKeyStore permission.");
}
private boolean publicKeysMatch(PublicKey publicKey, byte[] vaultParams) {
byte[] encodedPublicKey = SecureBox.encodePublicKey(publicKey);
return Arrays.equals(encodedPublicKey, Arrays.copyOf(vaultParams, encodedPublicKey.length));
}
}

View File

@@ -96,7 +96,26 @@ public class RecoverableKeyStoreManagerTest {
private static final byte[] TEST_SALT = getUtf8Bytes("salt");
private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault_params");
private static final byte[] TEST_VAULT_PARAMS = new byte[] {
// backend_key
(byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18, (byte) 0x98, (byte) 0x1d,
(byte) 0xf0, (byte) 0x6e, (byte) 0xb4, (byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda,
(byte) 0x1c, (byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a, (byte) 0xf6,
(byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0, (byte) 0x46, (byte) 0x49, (byte) 0x95,
(byte) 0x0f, (byte) 0x10, (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
(byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79, (byte) 0x20, (byte) 0x1d,
(byte) 0x91, (byte) 0x55, (byte) 0xb0, (byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b,
(byte) 0x32, (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc, (byte) 0xa5,
(byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21, (byte) 0x44, (byte) 0x7e, (byte) 0x78,
(byte) 0x52, (byte) 0xfa,
// counter_id
(byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00,
// device_parameter
(byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x0,
// max_attempts
(byte) 0x0a, (byte) 0x00, (byte) 0x00, (byte) 0x00};
private static final int TEST_GENERATION_ID = 2;
private static final int TEST_USER_ID = 10009;
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
@@ -248,6 +267,28 @@ public class RecoverableKeyStoreManagerTest {
}
}
@Test
public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
byte[] vaultParams = TEST_VAULT_PARAMS.clone();
vaultParams[1] ^= (byte) 1; // Flip 1 bit
try {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
TEST_PUBLIC_KEY,
vaultParams,
TEST_VAULT_CHALLENGE,
ImmutableList.of(
new KeyStoreRecoveryMetadata(
TYPE_LOCKSCREEN,
TYPE_PASSWORD,
KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
TEST_SECRET)));
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).contains("do not match");
}
}
@Test
public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
try {