diff options
author | Shawn Willden <swillden@google.com> | 2015-06-11 15:50:04 -0600 |
---|---|---|
committer | Shawn Willden <swillden@google.com> | 2015-06-12 10:31:48 -0600 |
commit | c7fe06da6dd4a7e5416a8b68a57ba563fbce72a0 (patch) | |
tree | d8dd494d77de2d63328bf51acd747efc1be33cd7 | |
parent | bfd9ed7f5c50cdfa310cb0f21c7706e99b780738 (diff) | |
download | keymaster-c7fe06da6dd4a7e5416a8b68a57ba563fbce72a0.tar.gz |
Restore support for old unversioned OCB-encrypted blobs.
This support was inadvertently removed in a refactor. There aren't many
of these keys around, since they were only created by pre-release
verions of Nexus 9 software, but we'll support them anyway.
Change-Id: I46c4e9a2866c02b8030d7aef97bb64c45441168b
-rw-r--r-- | android_keymaster_test.cpp | 17 | ||||
-rw-r--r-- | auth_encrypted_key_blob.cpp | 59 | ||||
-rw-r--r-- | km1_sw_rsa_512_unversioned.blob | bin | 0 -> 477 bytes |
3 files changed, 74 insertions, 2 deletions
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp index 2b4f5db..afc21c2 100644 --- a/android_keymaster_test.cpp +++ b/android_keymaster_test.cpp @@ -2562,6 +2562,23 @@ TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1RsaBlob) { EXPECT_EQ(0, GetParam()->keymaster0_calls()); } +TEST_P(Keymaster0AdapterTest, UnversionedSoftwareKeymaster1RsaBlob) { + // Load and use an old-style Keymaster1 software key blob, without the version byte. These + // blobs contain OCB-encrypted key data. + string km1_sw = read_file("km1_sw_rsa_512_unversioned.blob"); + EXPECT_EQ(477U, km1_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length())); + memcpy(key_data, km1_sw.data(), km1_sw.length()); + set_key_blob(key_data, km1_sw.length()); + + string message(64, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1EcdsaBlob) { // Load and use an old-style Keymaster1 software key blob. These blobs contain OCB-encrypted // key data. diff --git a/auth_encrypted_key_blob.cpp b/auth_encrypted_key_blob.cpp index eb9e2ca..64e0d63 100644 --- a/auth_encrypted_key_blob.cpp +++ b/auth_encrypted_key_blob.cpp @@ -54,6 +54,32 @@ keymaster_error_t SerializeAuthEncryptedBlob(const KeymasterKeyBlob& encrypted_k return KM_ERROR_OK; } +static keymaster_error_t DeserializeUnversionedBlob(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* encrypted_key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced, Buffer* nonce, + Buffer* tag) { + const uint8_t* tmp = key_blob.key_material; + const uint8_t** buf_ptr = &tmp; + const uint8_t* end = tmp + key_blob.key_material_size; + + if (!nonce->reserve(OCB_NONCE_LENGTH) || !tag->reserve(OCB_TAG_LENGTH)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!copy_from_buf(buf_ptr, end, nonce->peek_write(), OCB_NONCE_LENGTH) || + !encrypted_key_material->Deserialize(buf_ptr, end) || + !copy_from_buf(buf_ptr, end, tag->peek_write(), OCB_TAG_LENGTH) || + !hw_enforced->Deserialize(buf_ptr, end) || // + !sw_enforced->Deserialize(buf_ptr, end)) { + LOG_E("Failed to deserialize unversioned blob", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + nonce->advance_write(OCB_NONCE_LENGTH); + tag->advance_write(OCB_TAG_LENGTH); + + return KM_ERROR_OK; +} + keymaster_error_t DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob, KeymasterKeyBlob* encrypted_key_material, AuthorizationSet* hw_enforced, @@ -69,8 +95,37 @@ keymaster_error_t DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob, !encrypted_key_material->Deserialize(buf_ptr, end) || // !tag->Deserialize(buf_ptr, end) || tag->available_read() != OCB_TAG_LENGTH || !hw_enforced->Deserialize(buf_ptr, end) || // - !sw_enforced->Deserialize(buf_ptr, end)) - return KM_ERROR_INVALID_KEY_BLOB; + !sw_enforced->Deserialize(buf_ptr, end)) { + // This blob failed to parse. Either it's corrupted or it's a blob generated by an earlier + // version of keymaster using a previous blob format which did not include the version byte + // or the nonce or tag length fields. So we try to parse it as that previous version. + // + // Note that it's not really a problem if we erronously parse a corrupted blob, because + // decryption will fail the authentication check. + // + // A bigger potential problem is: What if a valid unversioned blob appears to parse + // correctly as a versioned blob? It would then be rejected during decryption, causing a + // valid key to become unusable. If this is a disk encryption key, upgrading to a keymaster + // version with the new format would destroy the user's data. + // + // What is the probability that an unversioned key could be successfully parsed as a version + // 0 key? The first 12 bytes of an unversioned key are the nonce, which, in the only + // keymaster version released with unversioned keys, is chosen randomly. In order for an + // unversioned key to parse as a version 0 key, the following must be true about the first + // five of those random bytes: + // + // 1. The first byte must be zero. This will happen with probability 1/2^8. + // + // 2. The second through fifth bytes must contain an unsigned integer value equal to + // NONCE_LENGTH. This will happen with probability 1/2^32. + // + // Based on those two checks alone, the probability of interpreting an unversioned blob as a + // version 0 blob is 1/2^40. That's small enough to be negligible, but there are additional + // checks which lower it further. + LOG_I("Failed to deserialize versioned key blob. Assuming unversioned.", 0); + return DeserializeUnversionedBlob(key_blob, encrypted_key_material, hw_enforced, + sw_enforced, nonce, tag); + } return KM_ERROR_OK; } diff --git a/km1_sw_rsa_512_unversioned.blob b/km1_sw_rsa_512_unversioned.blob Binary files differnew file mode 100644 index 0000000..e4f6a11 --- /dev/null +++ b/km1_sw_rsa_512_unversioned.blob |