summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Willden <swillden@google.com>2018-01-02 10:53:39 -0700
committerShawn Willden <swillden@google.com>2018-01-07 20:58:38 -0700
commit8b940582387a8a8f35584bd557b01a8b87610481 (patch)
treedb2199b79845a130ef5cd3f2dc0b7f15365e73f9
parent92a05fd05f0b8185dfb71923d107958148647b4e (diff)
downloadandroid_system_keymaster-8b940582387a8a8f35584bd557b01a8b87610481.tar.gz
android_system_keymaster-8b940582387a8a8f35584bd557b01a8b87610481.tar.bz2
android_system_keymaster-8b940582387a8a8f35584bd557b01a8b87610481.zip
Implement HMAC sharing in Android keymaster.
Test: make tests/android_keymaster_test.run Change-Id: I5372b97e97a2e13bd551c422bb15d27246d8cb47
-rw-r--r--Android.bp1
-rw-r--r--Makefile3
-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/soft_keymaster_enforcement.h23
-rw-r--r--km_openssl/soft_keymaster_enforcement.cpp94
-rw-r--r--ng/AndroidKeymaster4Device.cpp55
-rw-r--r--tests/android_keymaster_test.cpp210
12 files changed, 565 insertions, 41 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 24ce3f6..ce76218 100644
--- a/Makefile
+++ b/Makefile
@@ -349,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 \
@@ -385,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/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/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 e5e8e4f..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 {
@@ -3520,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