summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2018-01-10 21:46:13 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2018-01-10 21:46:13 +0000
commitcea25551e70df1d834050209a1b6a9fcf16a278a (patch)
tree22903c1e91e2fa6276dfe14b374fabcbb2be1b13
parented45c3c8bab0de7d974c801ec553e8927aa678d9 (diff)
parent8b940582387a8a8f35584bd557b01a8b87610481 (diff)
downloadkeymaster-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.bp1
-rw-r--r--Makefile22
-rw-r--r--android_keymaster/android_keymaster.cpp25
-rw-r--r--android_keymaster/android_keymaster_messages.cpp70
-rw-r--r--include/keymaster/android_keymaster.h3
-rw-r--r--include/keymaster/android_keymaster_messages.h95
-rw-r--r--include/keymaster/android_keymaster_utils.h13
-rw-r--r--include/keymaster/keymaster_enforcement.h14
-rw-r--r--include/keymaster/km_openssl/ckdf.h43
-rw-r--r--include/keymaster/km_openssl/soft_keymaster_enforcement.h23
-rw-r--r--km_openssl/ckdf.cpp124
-rw-r--r--km_openssl/soft_keymaster_enforcement.cpp94
-rw-r--r--ng/AndroidKeymaster4Device.cpp55
-rw-r--r--tests/android_keymaster_test.cpp218
-rw-r--r--tests/ckdf_test.cpp103
15 files changed, 856 insertions, 47 deletions
diff --git a/Android.bp b/Android.bp
index 27d0549..5eb1102 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
diff --git a/Makefile b/Makefile
index 3ffed52..ce76218 100644
--- a/Makefile
+++ b/Makefile
@@ -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