diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-01-10 21:46:13 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-01-10 21:46:13 +0000 |
commit | cea25551e70df1d834050209a1b6a9fcf16a278a (patch) | |
tree | 22903c1e91e2fa6276dfe14b374fabcbb2be1b13 | |
parent | ed45c3c8bab0de7d974c801ec553e8927aa678d9 (diff) | |
parent | 8b940582387a8a8f35584bd557b01a8b87610481 (diff) | |
download | keymaster-cea25551e70df1d834050209a1b6a9fcf16a278a.tar.gz |
Merge changes I5372b97e,Id751126d,Ia436694c
* changes:
Implement HMAC sharing in Android keymaster.
Partially fix keymaster unit tests.
Add CKDF implementation.
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | Makefile | 22 | ||||
-rw-r--r-- | android_keymaster/android_keymaster.cpp | 25 | ||||
-rw-r--r-- | android_keymaster/android_keymaster_messages.cpp | 70 | ||||
-rw-r--r-- | include/keymaster/android_keymaster.h | 3 | ||||
-rw-r--r-- | include/keymaster/android_keymaster_messages.h | 95 | ||||
-rw-r--r-- | include/keymaster/android_keymaster_utils.h | 13 | ||||
-rw-r--r-- | include/keymaster/keymaster_enforcement.h | 14 | ||||
-rw-r--r-- | include/keymaster/km_openssl/ckdf.h | 43 | ||||
-rw-r--r-- | include/keymaster/km_openssl/soft_keymaster_enforcement.h | 23 | ||||
-rw-r--r-- | km_openssl/ckdf.cpp | 124 | ||||
-rw-r--r-- | km_openssl/soft_keymaster_enforcement.cpp | 94 | ||||
-rw-r--r-- | ng/AndroidKeymaster4Device.cpp | 55 | ||||
-rw-r--r-- | tests/android_keymaster_test.cpp | 218 | ||||
-rw-r--r-- | tests/ckdf_test.cpp | 103 |
15 files changed, 856 insertions, 47 deletions
@@ -78,6 +78,7 @@ cc_library_shared { "km_openssl/asymmetric_key.cpp", "km_openssl/asymmetric_key_factory.cpp", "km_openssl/attestation_record.cpp", + "km_openssl/ckdf.cpp", "km_openssl/ec_key.cpp", "km_openssl/ec_key_factory.cpp", "km_openssl/ecdsa_operation.cpp", @@ -42,8 +42,8 @@ ifdef USE_GCC CXXFLAGS +=-std=c++14 -fprofile-arcs -ftest-coverage CFLAGS += -fprofile-arcs -ftest-coverage else -CC=$(BASE)/prebuilts/clang/host/linux-x86/clang-stable/bin/clang -CXX=$(BASE)/prebuilts/clang/host/linux-x86/clang-stable/bin/clang++ +CC=$(BASE)/prebuilts/clang/host/linux-x86/clang-4053586/bin/clang +CXX=$(BASE)/prebuilts/clang/host/linux-x86/clang-4053586/bin/clang++ CXXFLAGS +=-std=c++14 -DKEYMASTER_CLANG_TEST_BUILD CFLAGS += -DKEYMASTER_CLANG_TEST_BUILD endif @@ -85,6 +85,8 @@ CPPSRCS=\ km_openssl/ecies_kem.cpp \ tests/ecies_kem_test.cpp \ tests/gtest_main.cpp \ + km_openssl/ckdf.cpp \ + tests/hkdf_test.cpp \ km_openssl/hkdf.cpp \ tests/hkdf_test.cpp \ km_openssl/hmac.cpp \ @@ -141,6 +143,7 @@ BINARIES = \ tests/attestation_record_test \ tests/authorization_set_test \ tests/ecies_kem_test \ + tests/ckdf_test \ tests/hkdf_test \ tests/hmac_test \ tests/kdf1_test \ @@ -215,6 +218,17 @@ tests/hmac_test: tests/hmac_test.o \ android_keymaster/serializable.o \ $(GTEST_OBJS) +tests/ckdf_test: tests/ckdf_test.o \ + tests/android_keymaster_test_utils.o \ + android_keymaster/android_keymaster_utils.o \ + android_keymaster/authorization_set.o \ + android_keymaster/keymaster_tags.o \ + android_keymaster/logger.o \ + android_keymaster/serializable.o \ + km_openssl/ckdf.o \ + km_openssl/openssl_err.o \ + $(GTEST_OBJS) + tests/hkdf_test: tests/hkdf_test.o \ tests/android_keymaster_test_utils.o \ android_keymaster/android_keymaster_utils.o \ @@ -335,6 +349,7 @@ tests/android_keymaster_test: tests/android_keymaster_test.o \ km_openssl/ecdsa_operation.o \ km_openssl/hmac_key.o \ km_openssl/hmac_operation.o \ + km_openssl/ckdf.o \ key_blob_utils/integrity_assured_key_blob.o \ legacy_support/keymaster0_engine.o \ legacy_support/keymaster1_engine.o \ @@ -362,7 +377,6 @@ tests/android_keymaster_test: tests/android_keymaster_test.o \ contexts/soft_attestation_cert.o \ km_openssl/attestation_utils.o \ key_blob_utils/software_keyblobs.o \ - $(BASE)/system/security/softkeymaster/keymaster_openssl.o \ $(BASE)/system/security/keystore/keyblob_utils.o \ $(GTEST_OBJS) @@ -372,6 +386,8 @@ tests/keymaster_enforcement_test: tests/keymaster_enforcement_test.o \ android_keymaster/android_keymaster_utils.o \ android_keymaster/authorization_set.o \ android_keymaster/keymaster_enforcement.o \ + km_openssl/ckdf.o \ + km_openssl/openssl_err.o \ km_openssl/soft_keymaster_enforcement.o \ android_keymaster/keymaster_tags.o \ android_keymaster/logger.o \ diff --git a/android_keymaster/android_keymaster.cpp b/android_keymaster/android_keymaster.cpp index c21f381..ce7e42b 100644 --- a/android_keymaster/android_keymaster.cpp +++ b/android_keymaster/android_keymaster.cpp @@ -169,6 +169,31 @@ void AndroidKeymaster::SupportedExportFormats(const SupportedExportFormatsReques response->SetResults(formats, count); } +GetHmacSharingParametersResponse AndroidKeymaster::GetHmacSharingParameters() { + GetHmacSharingParametersResponse response; + KeymasterEnforcement* policy = context_->enforcement_policy(); + if (!policy) { + response.error = KM_ERROR_UNIMPLEMENTED; + return response; + } + + response.error = policy->GetHmacSharingParameters(&response.params); + return response; +} + +ComputeSharedHmacResponse +AndroidKeymaster::ComputeSharedHmac(const ComputeSharedHmacRequest& request) { + ComputeSharedHmacResponse response; + KeymasterEnforcement* policy = context_->enforcement_policy(); + if (!policy) { + response.error = KM_ERROR_UNIMPLEMENTED; + return response; + } + + response.error = policy->ComputeSharedHmac(request.params_array, &response.sharing_check); + return response; +} + void AndroidKeymaster::AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response) { response->error = context_->AddRngEntropy(request.random_data.peek_read(), diff --git a/android_keymaster/android_keymaster_messages.cpp b/android_keymaster/android_keymaster_messages.cpp index 77276d1..d6261eb 100644 --- a/android_keymaster/android_keymaster_messages.cpp +++ b/android_keymaster/android_keymaster_messages.cpp @@ -50,6 +50,24 @@ static bool deserialize_key_blob(keymaster_key_blob_t* key_blob, const uint8_t** return true; } +static size_t blob_size(const keymaster_blob_t& blob) { + return sizeof(uint32_t) /* data size */ + blob.data_length; +} + +static uint8_t* serialize_blob(const keymaster_blob_t& blob, uint8_t* buf, const uint8_t* end) { + return append_size_and_data_to_buf(buf, end, blob.data, blob.data_length); +} + +static bool deserialize_blob(keymaster_blob_t* blob, const uint8_t** buf_ptr, const uint8_t* end) { + delete[] blob->data; + *blob = {}; + UniquePtr<uint8_t[]> deserialized_blob; + if (!copy_size_and_data_from_buf(buf_ptr, end, &blob->data_length, &deserialized_blob)) + return false; + blob->data = deserialized_blob.release(); + return true; +} + size_t KeymasterResponse::SerializedSize() const { if (error != KM_ERROR_OK) return sizeof(int32_t); @@ -561,4 +579,56 @@ bool UpgradeKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint return deserialize_key_blob(&upgraded_key, buf_ptr, end); } +size_t HmacSharingParameters::SerializedSize() const { + return blob_size(seed) + sizeof(nonce); +} + +uint8_t* HmacSharingParameters::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = serialize_blob(seed, buf, end); + return append_to_buf(buf, end, nonce, sizeof(nonce)); +} + +bool HmacSharingParameters::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_blob(&seed, buf_ptr, end) && + copy_from_buf(buf_ptr, end, nonce, sizeof(nonce)); +} + +size_t HmacSharingParametersArray::SerializedSize() const { + size_t size = sizeof(uint32_t); // num_params size + for (size_t i = 0; i < num_params; ++i) { + size += params_array[i].SerializedSize(); + } + return size; +} + +uint8_t* HmacSharingParametersArray::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint32_to_buf(buf, end, num_params); + for (size_t i = 0; i < num_params; ++i) { + buf = params_array[i].Serialize(buf, end); + } + return buf; +} + +bool HmacSharingParametersArray::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + if (!copy_uint32_from_buf(buf_ptr, end, &num_params)) return false; + params_array = new (std::nothrow) HmacSharingParameters[num_params]; + if (!params_array) return false; + for (size_t i = 0; i < num_params; ++i) { + if (!params_array[i].Deserialize(buf_ptr, end)) return false; + } + return true; +} + +size_t ComputeSharedHmacResponse::NonErrorSerializedSize() const { + return blob_size(sharing_check); +} + +uint8_t* ComputeSharedHmacResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + return serialize_blob(sharing_check, buf, end); +} + +bool ComputeSharedHmacResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_blob(&sharing_check, buf_ptr, end); +} + } // namespace keymaster diff --git a/include/keymaster/android_keymaster.h b/include/keymaster/android_keymaster.h index d1abbae..64bc107 100644 --- a/include/keymaster/android_keymaster.h +++ b/include/keymaster/android_keymaster.h @@ -64,6 +64,9 @@ class AndroidKeymaster { void SupportedExportFormats(const SupportedExportFormatsRequest& request, SupportedExportFormatsResponse* response); + GetHmacSharingParametersResponse GetHmacSharingParameters(); + ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request); + void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response); void Configure(const ConfigureRequest& request, ConfigureResponse* response); void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response); diff --git a/include/keymaster/android_keymaster_messages.h b/include/keymaster/android_keymaster_messages.h index 5a42d6a..924d8e0 100644 --- a/include/keymaster/android_keymaster_messages.h +++ b/include/keymaster/android_keymaster_messages.h @@ -18,6 +18,7 @@ #define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_MESSAGES_H_ #include <assert.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -27,7 +28,7 @@ namespace keymaster { // Commands -enum AndroidKeymasterCommand { +enum AndroidKeymasterCommand : uint32_t { GENERATE_KEY = 0, BEGIN_OPERATION = 1, UPDATE_OPERATION = 2, @@ -47,6 +48,8 @@ enum AndroidKeymasterCommand { ATTEST_KEY = 16, UPGRADE_KEY = 17, CONFIGURE = 18, + GET_HMAC_SHARING_PARAMETERS = 19, + COMPUTE_SHARED_HMAC = 20, }; /** @@ -95,6 +98,7 @@ inline int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, uint8_t /* s struct KeymasterMessage : public Serializable { explicit KeymasterMessage(int32_t ver) : message_version(ver) { assert(ver >= 0); } + KeymasterMessage(KeymasterMessage&& other) : message_version(move(other.message_version)) {} uint32_t message_version; }; @@ -107,6 +111,9 @@ struct KeymasterMessage : public Serializable { struct KeymasterResponse : public KeymasterMessage { explicit KeymasterResponse(int32_t ver) : KeymasterMessage(ver), error(KM_ERROR_UNKNOWN_ERROR) {} + KeymasterResponse(KeymasterResponse&& other) + : KeymasterMessage(move(other)), error(move(other.error)) {} + KeymasterResponse& operator=(KeymasterResponse&&) = default; size_t SerializedSize() const override; uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; @@ -216,8 +223,7 @@ template <typename T> struct SupportedResponse : public KeymasterResponse { delete[] results; results = nullptr; UniquePtr<T[]> tmp; - if (!copy_uint32_array_from_buf(buf_ptr, end, &tmp, &results_length)) - return false; + if (!copy_uint32_array_from_buf(buf_ptr, end, &tmp, &results_length)) return false; results = tmp.release(); return true; } @@ -680,6 +686,89 @@ struct ConfigureResponse : public KeymasterResponse { bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; } }; +struct HmacSharingParameters : public Serializable { + HmacSharingParameters() : seed({}) { memset(nonce, 0, sizeof(nonce)); } + HmacSharingParameters(HmacSharingParameters&& other) { + seed = move(other.seed); + memcpy(nonce, other.nonce, sizeof(nonce)); + } + + void SetSeed(KeymasterBlob&& value) { seed = move(value); } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + KeymasterBlob seed{}; + uint8_t nonce[32]; +}; + +struct HmacSharingParametersArray : public Serializable { + HmacSharingParametersArray() : params_array(nullptr), num_params(0) {} + HmacSharingParametersArray(HmacSharingParametersArray&& other) { + delete[] params_array; + params_array = other.params_array; + num_params = other.num_params; + other.params_array = nullptr; + other.num_params = 0; + } + ~HmacSharingParametersArray() override { delete[] params_array; } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + HmacSharingParameters* params_array; + size_t num_params; +}; + +struct GetHmacSharingParametersResponse : public KeymasterResponse { + explicit GetHmacSharingParametersResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver) {} + GetHmacSharingParametersResponse(GetHmacSharingParametersResponse&& other) + : KeymasterResponse(other.message_version), params(move(other.params)) {} + + void SetSeed(KeymasterBlob&& seed_data) { params.SetSeed(move(seed_data)); } + + size_t NonErrorSerializedSize() const override { return params.SerializedSize(); } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override { + return params.Serialize(buf, end); + } + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return params.Deserialize(buf_ptr, end); + } + + HmacSharingParameters params; +}; + +struct ComputeSharedHmacRequest : public KeymasterMessage { + explicit ComputeSharedHmacRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return params_array.SerializedSize(); } + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override { + return params_array.Serialize(buf, end); + } + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return params_array.Deserialize(buf_ptr, end); + } + + HmacSharingParametersArray params_array; +}; + +struct ComputeSharedHmacResponse : public KeymasterResponse { + explicit ComputeSharedHmacResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver) {} + ComputeSharedHmacResponse(ComputeSharedHmacResponse&& other) : KeymasterResponse(move(other)) { + sharing_check = move(other.sharing_check); + } + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + KeymasterBlob sharing_check; +}; + } // namespace keymaster #endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_MESSAGES_H_ diff --git a/include/keymaster/android_keymaster_utils.h b/include/keymaster/android_keymaster_utils.h index 0f8acd4..67bf98d 100644 --- a/include/keymaster/android_keymaster_utils.h +++ b/include/keymaster/android_keymaster_utils.h @@ -422,6 +422,19 @@ constexpr T&& forward(remove_reference_t<T>&& x) { return static_cast<T&&>(x); } +template <class F> class final_action { + public: + explicit final_action(F f) : f_(move(f)) {} + ~final_action() { f_(); } + + private: + F f_; +}; + +template <class F> inline final_action<F> finally(const F& f) { + return final_action<F>(f); +} + } // namespace keymaster #endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_ diff --git a/include/keymaster/keymaster_enforcement.h b/include/keymaster/keymaster_enforcement.h index a9a5684..147b696 100644 --- a/include/keymaster/keymaster_enforcement.h +++ b/include/keymaster/keymaster_enforcement.h @@ -35,6 +35,8 @@ class KeymasterEnforcementContext { class AccessTimeMap; class AccessCountMap; +struct HmacSharingParameters; +struct HmacSharingParametersArray; class KeymasterEnforcement { public: @@ -135,6 +137,17 @@ class KeymasterEnforcement { virtual bool ValidateTokenSignature(const hw_auth_token_t& token) const = 0; /** + * Get the sharing parameters used to negotiate a shared HMAC key among multiple parties. + */ + virtual keymaster_error_t GetHmacSharingParameters(HmacSharingParameters* params) = 0; + + /** + * Compute an HMAC key shared among multiple parties. + */ + virtual keymaster_error_t ComputeSharedHmac(const HmacSharingParametersArray& params_array, + KeymasterBlob* sharingCheck) = 0; + + /** * Creates a key ID for use in subsequent calls to AuthorizeOperation. AndroidKeymaster uses * this method for creating key IDs. The generated id must be stable in that the same key_blob * bits yield the same keyid. @@ -143,7 +156,6 @@ class KeymasterEnforcement { */ virtual bool CreateKeyId(const keymaster_key_blob_t& key_blob, km_id_t* keyid) const = 0; - private: keymaster_error_t AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set, const AuthorizationSet& operation_params, diff --git a/include/keymaster/km_openssl/ckdf.h b/include/keymaster/km_openssl/ckdf.h new file mode 100644 index 0000000..7585929 --- /dev/null +++ b/include/keymaster/km_openssl/ckdf.h @@ -0,0 +1,43 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_KEYMASTER_CKDF_H_ +#define SYSTEM_KEYMASTER_CKDF_H_ + +#include <keymaster/android_keymaster_utils.h> + +namespace keymaster { + +/** + * Implementation of CKDF, aka AES-CMAC KDF, from NIST SP 800-108. Uses 32-bit i and L, and + * prefixes with i. This version takes the context in an array of keymaster_blob_ts. + */ +keymaster_error_t ckdf(const KeymasterKeyBlob& key, const KeymasterBlob& label, + const keymaster_blob_t* context_chunks, size_t num_chunks, + KeymasterKeyBlob* output); + +/** + * Implementation of CKDF, aka AES-CMAC KDF, from NIST SP 800-108. Uses 32-bit i and L, and + * prefixes with i. This version takes the context as a single keymaster_blob_t&. + */ +inline keymaster_error_t ckdf(const KeymasterKeyBlob& key, const KeymasterBlob& label, + const keymaster_blob_t& context_chunks, KeymasterKeyBlob* output) { + return ckdf(key, label, &context_chunks, 1 /* num_chunks */, output); +} + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KDF_H_ diff --git a/include/keymaster/km_openssl/soft_keymaster_enforcement.h b/include/keymaster/km_openssl/soft_keymaster_enforcement.h index 3b29685..a510009 100644 --- a/include/keymaster/km_openssl/soft_keymaster_enforcement.h +++ b/include/keymaster/km_openssl/soft_keymaster_enforcement.h @@ -18,25 +18,36 @@ #ifndef INCLUDE_KEYMASTER_SOFT_KEYMASTER_ENFORCEMENT_H_ #define INCLUDE_KEYMASTER_SOFT_KEYMASTER_ENFORCEMENT_H_ +#include <keymaster/android_keymaster_messages.h> #include <keymaster/keymaster_enforcement.h> namespace keymaster { class SoftKeymasterEnforcement : public KeymasterEnforcement { -public: - SoftKeymasterEnforcement (uint32_t max_access_time_map_size, uint32_t max_access_count_map_size) - : KeymasterEnforcement(max_access_time_map_size, max_access_count_map_size) {} - virtual~SoftKeymasterEnforcement() {} + public: + SoftKeymasterEnforcement(uint32_t max_access_time_map_size, uint32_t max_access_count_map_size) + : KeymasterEnforcement(max_access_time_map_size, max_access_count_map_size) {} + virtual ~SoftKeymasterEnforcement() {} bool activation_date_valid(uint64_t /*activation_date*/) const override { return true; } bool expiration_date_passed(uint64_t /*expiration_date*/) const override { return false; } - bool auth_token_timed_out(const hw_auth_token_t& /*token*/, uint32_t /*timeout*/) const override { + bool auth_token_timed_out(const hw_auth_token_t& /*token*/, + uint32_t /*timeout*/) const override { return false; } uint32_t get_current_time() const override; bool ValidateTokenSignature(const hw_auth_token_t& /*token*/) const override { return true; } bool CreateKeyId(const keymaster_key_blob_t& key_blob, km_id_t* keyid) const override; + + keymaster_error_t GetHmacSharingParameters(HmacSharingParameters* params) override; + keymaster_error_t ComputeSharedHmac(const HmacSharingParametersArray& params_array, + KeymasterBlob* sharingCheck) override; + + private: + bool have_saved_params_ = false; + HmacSharingParameters saved_params_; + KeymasterKeyBlob hmac_key_; }; -} // namespace keymaster +} // namespace keymaster #endif // INCLUDE_KEYMASTER_SOFT_KEYMASTER_ENFORCEMENT_H_ diff --git a/km_openssl/ckdf.cpp b/km_openssl/ckdf.cpp new file mode 100644 index 0000000..eaf636a --- /dev/null +++ b/km_openssl/ckdf.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <keymaster/km_openssl/ckdf.h> + +#include <assert.h> + +#include <openssl/aes.h> +#include <openssl/cmac.h> + +#include <keymaster/km_openssl/openssl_err.h> +#include <keymaster/km_openssl/openssl_utils.h> +#include <keymaster/serializable.h> + +namespace keymaster { + +inline uint32_t div_round_up(uint32_t dividend, uint32_t divisor) { + return (dividend + divisor - 1) / divisor; +} + +size_t min(size_t a, size_t b) { + return a < b ? a : b; +} + +DEFINE_OPENSSL_OBJECT_POINTER(CMAC_CTX) + +keymaster_error_t ckdf(const KeymasterKeyBlob& key, const KeymasterBlob& label, + const keymaster_blob_t* context_chunks, size_t num_chunks, + KeymasterKeyBlob* output) { + // Note: the variables i and L correspond to i and L in the standard. See page 12 of + // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf. + + const uint32_t blocks = div_round_up(output->key_material_size, AES_BLOCK_SIZE); + const uint32_t L = output->key_material_size * 8; // bits + const uint32_t net_order_L = hton(L); + + CMAC_CTX_Ptr ctx(CMAC_CTX_new()); + if (!ctx.get()) return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + auto algo = EVP_aes_128_cbc(); + switch (key.key_material_size) { + case AES_BLOCK_SIZE: + /* Already set */ + break; + case AES_BLOCK_SIZE * 2: + algo = EVP_aes_256_cbc(); + break; + default: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + + if (!CMAC_Init(ctx.get(), key.key_material, key.key_material_size, algo, + nullptr /* engine */)) { + return TranslateLastOpenSslError(); + } + + auto output_pos = const_cast<uint8_t*>(output->begin()); + memset(output_pos, 0, output->key_material_size); + for (uint32_t i = 1; i <= blocks; ++i) { + // Data to mac is i || label || 0x00 || context || L, with i and L represented in 32 bits, + // in network order. + + // i + uint32_t net_order_i = hton(i); + if (!CMAC_Update(ctx.get(), reinterpret_cast<uint8_t*>(&net_order_i), + sizeof(net_order_i))) { + return TranslateLastOpenSslError(); + } + + // label + if (!CMAC_Update(ctx.get(), label.data, label.data_length)) { + return TranslateLastOpenSslError(); + } + + // 0x00 + uint8_t zero = 0; + if (!CMAC_Update(ctx.get(), &zero, sizeof(zero))) return TranslateLastOpenSslError(); + + // context + for (size_t chunk = 0; chunk < num_chunks; ++chunk) { + if (!CMAC_Update(ctx.get(), context_chunks[chunk].data, + context_chunks[chunk].data_length)) { + return TranslateLastOpenSslError(); + } + } + + // L + uint8_t buf[4]; + memcpy(buf, &net_order_L, 4); + if (!CMAC_Update(ctx.get(), buf, sizeof(buf))) TranslateLastOpenSslError(); + + size_t out_len; + if (output_pos <= output->end() - AES_BLOCK_SIZE) { + if (!CMAC_Final(ctx.get(), output_pos, &out_len)) return TranslateLastOpenSslError(); + output_pos += out_len; + } else { + uint8_t cmac[AES_BLOCK_SIZE]; + if (!CMAC_Final(ctx.get(), cmac, &out_len)) return TranslateLastOpenSslError(); + size_t to_copy = output->end() - output_pos; + memcpy(output_pos, cmac, to_copy); + output_pos += to_copy; + } + + CMAC_Reset(ctx.get()); + } + assert(output_pos == output->end()); + + return KM_ERROR_OK; +} + +} // namespace keymaster diff --git a/km_openssl/soft_keymaster_enforcement.cpp b/km_openssl/soft_keymaster_enforcement.cpp index de03d38..5bd03d4 100644 --- a/km_openssl/soft_keymaster_enforcement.cpp +++ b/km_openssl/soft_keymaster_enforcement.cpp @@ -22,12 +22,23 @@ #include <limits> +#include <openssl/cmac.h> #include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> + +#include <keymaster/km_openssl/ckdf.h> +#include <keymaster/km_openssl/openssl_err.h> +#include <keymaster/km_openssl/openssl_utils.h> namespace keymaster { namespace { +constexpr uint8_t kFakeKeyAgreementKey[32] = {}; +constexpr const char* kSharedHmacLabel = "KeymasterSharedMac"; +constexpr const char* kMacVerificationString = "Keymaster HMAC Verification"; + class EvpMdCtx { public: EvpMdCtx() { EVP_MD_CTX_init(&ctx_); } @@ -39,19 +50,20 @@ class EvpMdCtx { EVP_MD_CTX ctx_; }; -} // anonymous namespace +} // anonymous namespace uint32_t SoftKeymasterEnforcement::get_current_time() const { struct timespec tp; int err = clock_gettime(CLOCK_MONOTONIC, &tp); if (err || tp.tv_sec < 0 || - static_cast<unsigned long>(tp.tv_sec) > std::numeric_limits<uint32_t>::max()) { + static_cast<unsigned long>(tp.tv_sec) > std::numeric_limits<uint32_t>::max()) { return 0; } return static_cast<uint32_t>(tp.tv_sec); } -bool SoftKeymasterEnforcement::CreateKeyId(const keymaster_key_blob_t& key_blob, km_id_t* keyid) const { +bool SoftKeymasterEnforcement::CreateKeyId(const keymaster_key_blob_t& key_blob, + km_id_t* keyid) const { EvpMdCtx ctx; uint8_t hash[EVP_MAX_MD_SIZE]; @@ -67,4 +79,78 @@ bool SoftKeymasterEnforcement::CreateKeyId(const keymaster_key_blob_t& key_blob, return false; } -} // namespace keymaster +keymaster_error_t +SoftKeymasterEnforcement::GetHmacSharingParameters(HmacSharingParameters* params) { + if (!have_saved_params_) { + saved_params_.seed = {}; + RAND_bytes(saved_params_.nonce, 32); + have_saved_params_ = true; + } + params->seed = saved_params_.seed; + memcpy(params->nonce, saved_params_.nonce, sizeof(params->nonce)); + return KM_ERROR_OK; +} + +keymaster_error_t hmacSha256(const keymaster_key_blob_t& key, const keymaster_blob_t& data, + KeymasterBlob* output) { + if (!output) return KM_ERROR_UNEXPECTED_NULL_POINTER; + + unsigned digest_len = SHA256_DIGEST_LENGTH; + if (!output->Reset(digest_len)) return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!::HMAC(EVP_sha256(), key.key_material, key.key_material_size, data.data, data.data_length, + output->writable_data(), &digest_len)) { + return TranslateLastOpenSslError(); + } + if (digest_len != output->data_length) return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +namespace { + +// Perhaps this shoud be in utils, but the impact of that needs to be considred carefully. For now, +// just define it here. +inline bool operator==(const keymaster_blob_t& a, const keymaster_blob_t& b) { + if (!a.data_length && !b.data_length) return true; + if (!(a.data && b.data)) return a.data == b.data; + return (a.data_length == b.data_length && !memcmp(a.data, b.data, a.data_length)); +} + +bool operator==(const HmacSharingParameters& a, const HmacSharingParameters& b) { + return a.seed == b.seed && !memcmp(a.nonce, b.nonce, sizeof(a.nonce)); +} + +} // namespace + +keymaster_error_t +SoftKeymasterEnforcement::ComputeSharedHmac(const HmacSharingParametersArray& params_array, + KeymasterBlob* sharingCheck) { + size_t num_chunks = params_array.num_params * 2; + UniquePtr<keymaster_blob_t[]> context_chunks(new (std::nothrow) keymaster_blob_t[num_chunks]); + if (!context_chunks.get()) return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + bool found_mine = false; + auto context_chunks_pos = context_chunks.get(); + for (auto& params : array_range(params_array.params_array, params_array.num_params)) { + *context_chunks_pos++ = params.seed; + *context_chunks_pos++ = {params.nonce, sizeof(params.nonce)}; + found_mine = found_mine || params == saved_params_; + } + assert(context_chunks_pos - num_chunks == context_chunks.get()); + + if (!found_mine) return KM_ERROR_INVALID_ARGUMENT; + + if (!hmac_key_.Reset(SHA256_DIGEST_LENGTH)) return KM_ERROR_MEMORY_ALLOCATION_FAILED; + keymaster_error_t error = ckdf( + KeymasterKeyBlob(kFakeKeyAgreementKey, sizeof(kFakeKeyAgreementKey)), + KeymasterBlob(reinterpret_cast<const uint8_t*>(kSharedHmacLabel), strlen(kSharedHmacLabel)), + context_chunks.get(), num_chunks, // + &hmac_key_); + if (error != KM_ERROR_OK) return error; + + keymaster_blob_t data = {reinterpret_cast<const uint8_t*>(kMacVerificationString), + strlen(kMacVerificationString)}; + return hmacSha256(hmac_key_, data, sharingCheck); +} + +} // namespace keymaster diff --git a/ng/AndroidKeymaster4Device.cpp b/ng/AndroidKeymaster4Device.cpp index ccc3a9e..9801ea4 100644 --- a/ng/AndroidKeymaster4Device.cpp +++ b/ng/AndroidKeymaster4Device.cpp @@ -30,28 +30,6 @@ #include <keymaster/keymaster_enforcement.h> #include <keymaster/km_openssl/soft_keymaster_enforcement.h> -using ::keymaster::AbortOperationRequest; -using ::keymaster::AbortOperationResponse; -using ::keymaster::AddEntropyRequest; -using ::keymaster::AddEntropyResponse; -using ::keymaster::AttestKeyRequest; -using ::keymaster::AttestKeyResponse; -using ::keymaster::AuthorizationSet; -using ::keymaster::BeginOperationRequest; -using ::keymaster::BeginOperationResponse; -using ::keymaster::ExportKeyRequest; -using ::keymaster::ExportKeyResponse; -using ::keymaster::FinishOperationRequest; -using ::keymaster::FinishOperationResponse; -using ::keymaster::GenerateKeyRequest; -using ::keymaster::GenerateKeyResponse; -using ::keymaster::GetKeyCharacteristicsRequest; -using ::keymaster::GetKeyCharacteristicsResponse; -using ::keymaster::ImportKeyRequest; -using ::keymaster::ImportKeyResponse; -using ::keymaster::UpdateOperationRequest; -using ::keymaster::UpdateOperationResponse; - namespace keymaster { namespace V4_0 { namespace ng { @@ -238,14 +216,37 @@ Return<void> AndroidKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_c Return<void> AndroidKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { - _hidl_cb(ErrorCode::UNIMPLEMENTED, {}); + auto response = impl_->GetHmacSharingParameters(); + + ::android::hardware::keymaster::V4_0::HmacSharingParameters params; + params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data), + response.params.seed.data_length); + static_assert(sizeof(response.params.nonce) == params.nonce.size(), "Nonce sizes don't match"); + memcpy(params.nonce.data(), response.params.nonce, params.nonce.size()); + _hidl_cb(legacy_enum_conversion(response.error), params); return Void(); } -Return<void> -AndroidKeymaster4Device::computeSharedHmac(const hidl_vec<HmacSharingParameters>& /* params */, - computeSharedHmac_cb _hidl_cb) { - _hidl_cb(ErrorCode::UNIMPLEMENTED, {}); +Return<void> AndroidKeymaster4Device::computeSharedHmac( + const hidl_vec<::android::hardware::keymaster::V4_0::HmacSharingParameters>& params, + computeSharedHmac_cb _hidl_cb) { + ComputeSharedHmacRequest request; + request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()]; + request.params_array.num_params = params.size(); + for (size_t i = 0; i < params.size(); ++i) { + request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()}; + static_assert(sizeof(request.params_array.params_array[i].nonce) == + decltype(params[i].nonce)::size(), + "Nonce sizes don't match"); + memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(), + params[i].nonce.size()); + } + + auto response = impl_->ComputeSharedHmac(request); + hidl_vec<uint8_t> sharing_check; + if (response.error == KM_ERROR_OK) sharing_check = kmBlob2hidlVec(response.sharing_check); + + _hidl_cb(legacy_enum_conversion(response.error), sharing_check); return Void(); } diff --git a/tests/android_keymaster_test.cpp b/tests/android_keymaster_test.cpp index 2afc4f4..152e107 100644 --- a/tests/android_keymaster_test.cpp +++ b/tests/android_keymaster_test.cpp @@ -15,6 +15,7 @@ */ #include <fstream> +#include <memory> #include <string> #include <vector> @@ -49,6 +50,15 @@ int __android_log_print(int prio, const char* tag, const char* fmt) { } } // extern "C" +namespace { + +// For some reason std::make_unique isn't available. Define make_unique. +template <typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +} // namespace + namespace keymaster { namespace test { @@ -1434,6 +1444,7 @@ TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndKeySizes) { string signature; for (auto key_size : key_sizes) { + SCOPED_TRACE(testing::Message() << "Key size: " << key_size); AuthorizationSetBuilder builder; builder.EcdsaSigningKey(key_size); for (auto digest : digests) @@ -1441,6 +1452,7 @@ TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndKeySizes) { ASSERT_EQ(KM_ERROR_OK, GenerateKey(builder)); for (auto digest : digests) { + SCOPED_TRACE(testing::Message() << "Digest: " << digest); SignMessage(message, &signature, digest); VerifyMessage(message, signature, digest); } @@ -3254,7 +3266,7 @@ static bool verify_attestation_record(const string& challenge, &att_keymaster_security_level, &att_challenge, &att_sw_enforced, &att_tee_enforced, &att_unique_id)); - EXPECT_EQ(1U, att_attestation_version); + EXPECT_EQ(2U, att_attestation_version); EXPECT_EQ(KM_SECURITY_LEVEL_SOFTWARE, att_attestation_security_level); EXPECT_EQ(expected_keymaster_version, att_keymaster_version); EXPECT_EQ(expected_keymaster_security_level, att_keymaster_security_level); @@ -3310,7 +3322,7 @@ TEST_P(AttestationTest, RsaAttestation) { expected_keymaster_version = 0; expected_keymaster_security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; } else { - expected_keymaster_version = 2; + expected_keymaster_version = 3; expected_keymaster_security_level = KM_SECURITY_LEVEL_SOFTWARE; } @@ -3332,7 +3344,7 @@ TEST_P(AttestationTest, EcAttestation) { expected_keymaster_version = 0; expected_keymaster_security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; } else { - expected_keymaster_version = 2; + expected_keymaster_version = 3; expected_keymaster_security_level = KM_SECURITY_LEVEL_SOFTWARE; } @@ -3518,5 +3530,205 @@ TEST(SoftKeymasterWrapperTest, CheckKeymaster2Device) { sha256_only_fake_wrapper->hw_device()); } +class HmacKeySharingTest : public ::testing::Test { + protected: + using KeymasterVec = std::vector<std::unique_ptr<AndroidKeymaster>>; + using ParamsVec = std::vector<HmacSharingParameters>; + using ByteString = std::basic_string<uint8_t>; + using NonceVec = std::vector<ByteString>; + using ResponseVec = std::vector<ComputeSharedHmacResponse>; + + KeymasterVec CreateKeymasters(size_t count) { + KeymasterVec keymasters; + for (size_t i = 0; i < count; ++i) { + keymasters.push_back(make_unique<AndroidKeymaster>(new TestKeymasterContext, 16)); + } + return keymasters; + } + + ParamsVec GetHmacSharingParameters(const KeymasterVec& keymasters) { + ParamsVec paramsVec; + for (auto& keymaster : keymasters) { + auto result = keymaster->GetHmacSharingParameters(); + EXPECT_EQ(KM_ERROR_OK, result.error); + if (result.error == KM_ERROR_OK) paramsVec.push_back(move(result.params)); + } + return paramsVec; + } + + template <size_t N> ByteString ToByteString(const uint8_t (&a)[N]) { return ByteString(a, N); } + + ByteString ToByteString(const keymaster_blob_t& b) { return ByteString(b.data, b.data_length); } + + NonceVec CopyNonces(const ParamsVec& paramsVec) { + NonceVec nonces; + for (auto& param : paramsVec) { + nonces.push_back(ToByteString(param.nonce)); + } + return nonces; + } + + ResponseVec ComputeSharedHmac(const KeymasterVec& keymasters, const ParamsVec& paramsVec) { + ComputeSharedHmacRequest req; + req.params_array.params_array = const_cast<HmacSharingParameters*>(paramsVec.data()); + auto prevent_deletion_of_paramsVec_data = + finally([&]() { req.params_array.params_array = nullptr; }); + req.params_array.num_params = paramsVec.size(); + + ResponseVec responses; + for (auto& keymaster : keymasters) { + responses.push_back(keymaster->ComputeSharedHmac(req)); + } + + return responses; + } + + bool VerifyResponses(const ByteString& expected, const ResponseVec& responses) { + for (auto& response : responses) { + EXPECT_EQ(KM_ERROR_OK, response.error); + auto this_sharing_check = ToByteString(response.sharing_check); + EXPECT_EQ(expected, this_sharing_check) << "Sharing check values should match."; + if (response.error != KM_ERROR_OK || expected != this_sharing_check) { + return false; + } + } + return true; + } +}; + +TEST_F(HmacKeySharingTest, GetParametersIdempotency) { + AndroidKeymaster keymaster(new TestKeymasterContext, 16); + + ParamsVec paramsVec; + + auto result1 = keymaster.GetHmacSharingParameters(); + EXPECT_EQ(KM_ERROR_OK, result1.error); + paramsVec.push_back(std::move(result1.params)); + + auto result2 = keymaster.GetHmacSharingParameters(); + EXPECT_EQ(KM_ERROR_OK, result2.error); + paramsVec.push_back(std::move(result2.params)); + + ASSERT_EQ(ToByteString(paramsVec[0].seed), ToByteString(paramsVec[1].seed)) + << "A given keymaster should always return the same seed."; + EXPECT_EQ(ToByteString(paramsVec[0].nonce), ToByteString(paramsVec[1].nonce)) + << "A given keymaster should always return the same nonce until restart."; +} + +TEST_F(HmacKeySharingTest, ComputeSharedHmac) { + // ComputeSharedHmac should work with any number of participants; we just test 1 through 4. + for (size_t keymaster_count = 1; keymaster_count <= 4; ++keymaster_count) { + SCOPED_TRACE(testing::Message() << keymaster_count << " keymaster instances"); + + auto keymasters = CreateKeymasters(keymaster_count); + auto params = GetHmacSharingParameters(keymasters); + ASSERT_EQ(keymaster_count, params.size()) + << "One or more keymasters failed to provide parameters."; + + auto nonces = CopyNonces(params); + EXPECT_EQ(keymaster_count, nonces.size()) << "We should have a nonce per keymaster."; + std::sort(nonces.begin(), nonces.end()); + std::unique(nonces.begin(), nonces.end()); + EXPECT_EQ(keymaster_count, nonces.size()) << "Nonces should all be unique."; + + auto responses = ComputeSharedHmac(keymasters, params); + ASSERT_EQ(keymaster_count, responses.size()); + + ASSERT_TRUE(VerifyResponses(ToByteString(responses[0].sharing_check), responses)); + } +} + +TEST_F(HmacKeySharingTest, ComputeSharedHmacTwice) { + for (size_t keymaster_count = 1; keymaster_count <= 4; ++keymaster_count) { + SCOPED_TRACE(testing::Message() << keymaster_count << " keymaster instances"); + + auto keymasters = CreateKeymasters(keymaster_count); + auto params = GetHmacSharingParameters(keymasters); + ASSERT_EQ(keymaster_count, params.size()) + << "One or more keymasters failed to provide parameters."; + + auto responses = ComputeSharedHmac(keymasters, params); + ASSERT_EQ(keymaster_count, responses.size()); + + ByteString sharing_check_value = ToByteString(responses[0].sharing_check); + ASSERT_TRUE(VerifyResponses(sharing_check_value, responses)); + + params = GetHmacSharingParameters(keymasters); + ASSERT_EQ(keymaster_count, params.size()) + << "One or more keymasters failed to provide parameters."; + responses = ComputeSharedHmac(keymasters, params); + + // Verify against first check value; we should get the same one every time, because each + // keymaster instance returns the same seed every time, and the same nonce until restart. + ASSERT_TRUE(VerifyResponses(sharing_check_value, responses)); + } +} + +TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptNonce) { + constexpr size_t keymaster_count = 4; + auto keymasters = CreateKeymasters(keymaster_count); + + auto params = GetHmacSharingParameters(keymasters); + ASSERT_EQ(keymaster_count, params.size()) + << "One or more keymasters failed to provide parameters."; + + // All should be well in the normal case + auto responses = ComputeSharedHmac(keymasters, params); + ASSERT_EQ(keymaster_count, responses.size()); + ByteString sharing_check_value = ToByteString(responses[0].sharing_check); + ASSERT_TRUE(VerifyResponses(sharing_check_value, responses)); + + // Pick a random param, a random byte within the param's nonce, and a random bit within + // the byte. Flip that bit. + size_t param_to_tweak = rand() % params.size(); + uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce); + uint8_t bit_to_tweak = rand() % 8; + params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak); + + responses = ComputeSharedHmac(keymasters, params); + EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, responses[param_to_tweak].error) + << "Keymaster that provided tweaked response should fail to compute HMAC key"; + for (size_t i = 0; i < responses.size(); ++i) { + if (i != param_to_tweak) { + EXPECT_EQ(KM_ERROR_OK, responses[i].error) << "Others should succeed"; + EXPECT_NE(sharing_check_value, ToByteString(responses[i].sharing_check)) + << "Others should calculate a different HMAC key, due to the tweaked nonce."; + } + } +} + +TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptSeed) { + constexpr size_t keymaster_count = 4; + auto keymasters = CreateKeymasters(keymaster_count); + + auto params = GetHmacSharingParameters(keymasters); + ASSERT_EQ(keymaster_count, params.size()) + << "One or more keymasters failed to provide parameters."; + + // All should be well in the normal case + auto responses = ComputeSharedHmac(keymasters, params); + ASSERT_EQ(keymaster_count, responses.size()); + ByteString sharing_check_value = ToByteString(responses[0].sharing_check); + ASSERT_TRUE(VerifyResponses(sharing_check_value, responses)); + + // Pick a random param and modify the seed. + auto param_to_tweak = rand() & params.size(); + constexpr uint8_t wrong_seed_value[] = {0xF, 0x0, 0x0}; + params[param_to_tweak].SetSeed({wrong_seed_value, sizeof(wrong_seed_value)}); + auto prevent_deletion_of_wrong_seed = + finally([&]() { params[param_to_tweak].seed.data = nullptr; }); + + responses = ComputeSharedHmac(keymasters, params); + EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, responses[param_to_tweak].error) + << "Keymaster that provided tweaked response should fail to compute HMAC key"; + for (size_t i = 0; i < responses.size(); ++i) { + if (i != param_to_tweak) { + EXPECT_EQ(KM_ERROR_OK, responses[i].error) << "Others should succeed"; + EXPECT_NE(sharing_check_value, ToByteString(responses[i].sharing_check)) + << "Others should calculate a different HMAC key, due to the tweaked seed."; + } + } +} + } // namespace test } // namespace keymaster diff --git a/tests/ckdf_test.cpp b/tests/ckdf_test.cpp new file mode 100644 index 0000000..118a320 --- /dev/null +++ b/tests/ckdf_test.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <keymaster/km_openssl/ckdf.h> + +#include <gtest/gtest.h> +#include <string.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { +namespace test { + +struct CkdfTest { + const char* key; + const char* label; + const char* context; + const char* output; +}; + +static const CkdfTest kCkdfTests[] = {{"80583f389dd797a3d18abd7b9399da02" + "6fecb1eade7bc2f0ef091ad39e613c35", + "c16e6e02c5a3dcc8d78b9ac1306877761310455b4e41469951d9e6", + "c2245a064b33fd8c3b01203a7824485bf0a64060c4648b707d260793", + "5d42a1941670917d746c7278e75f4879" + "750469dcb59c129e42edb7a3273f38d4" + "ea6fbcba9f422f735fc2db23603c63e5" + "86ff39cc048f4bc18690e478dd1108fa" + "fc635b29acb6b29784fdf8184296fa7f" + "772b62cdd1a8bd1a2d073830fac0409b" + "45acedf53a70676de96d7cb7e337cec4" + "08d5e3d626ac6c775baf71368b1d5851" + "47585f06b305ad5f547cb40644d2e048" + "7a9ded9778ddbfac15a6a7aee399fc7d" + "92610b028c624fd68cb573b830d842c2" + "ceb34da13efd50db13165a4f19d38cea" + "3293a073ba2d1bb31642297764b0fc17" + "e941ba73d703ba77455f30f9293a41fe" + "2915358c99f95a55075811d57ddff3d3" + "67d0a59e5b2f4e1c697b1e9955aa972c" + "f43d5b81c242a2b8eda917b25dc689be" + "f514b39979b7181eb5db62eb39cd0c3a" + "3dcb8013b19bdb262a890fce3360a351" + "cb3ddf76c13606177479b6e1345a2705" + "eaf97715af161b17b715ab6ef006e697" + "a1a779ea879a10c258069c4d9522d411" + "70aa69132d6e5cecb7ada5d16973d77f" + "3d7cc647175604d7151480473e61e73f" + "36227324058f38f578198a19e083db2b" + "8454ee2a00b527a99e3ec9addbfbd3a6" + "8c51cab16a720b7f47fe6fbfb4ca541c" + "2ec4683588ce2106fc907d987620ee48" + "aa506b8a246a18e2fa156d66b5add15c" + "2305615bb1c7c76d95aa679545eac38b" + "806cd02e5ef89897e278a536c25553f4" + "05d12474"}}; + +template <class Blob> Blob hex2span(const char* hex) { + string bytes = hex2str(hex); + Blob retval(reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size()); + return std::move(retval); +} + +KeymasterKeyBlob hex2key(const char* hex) { + return hex2span<KeymasterKeyBlob>(hex); +} + +KeymasterBlob hex2blob(const char* hex) { + return hex2span<KeymasterBlob>(hex); +} + +TEST(CkdfTest, Ckdf) { + for (auto& test : kCkdfTests) { + auto key = hex2key(test.key); + auto label = hex2blob(test.label); + auto context = hex2blob(test.context); + auto expected = hex2blob(test.output); + + KeymasterKeyBlob output; + output.Reset(expected.data_length); + + ASSERT_EQ(KM_ERROR_OK, ckdf(key, label, context, &output)); + EXPECT_TRUE(std::equal(output.begin(), output.end(), expected.begin())); + } +} + +} // namespace test +} // namespace keymaster |