summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Willden <swillden@google.com>2015-06-11 15:50:04 -0600
committerShawn Willden <swillden@google.com>2015-06-12 10:31:48 -0600
commitc7fe06da6dd4a7e5416a8b68a57ba563fbce72a0 (patch)
treed8dd494d77de2d63328bf51acd747efc1be33cd7
parentbfd9ed7f5c50cdfa310cb0f21c7706e99b780738 (diff)
downloadkeymaster-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.cpp17
-rw-r--r--auth_encrypted_key_blob.cpp59
-rw-r--r--km1_sw_rsa_512_unversioned.blobbin0 -> 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
new file mode 100644
index 0000000..e4f6a11
--- /dev/null
+++ b/km1_sw_rsa_512_unversioned.blob
Binary files differ