From dda22ea8845f6906d4bccc13bf142d96a43976c5 Mon Sep 17 00:00:00 2001 From: Shawn Willden Date: Fri, 2 Dec 2016 05:07:02 -0700 Subject: [PATCH] Add keymaster VTS tests. Note: Recent Google devices have keymaster implementations that don't pass this test suite. See https://goo.gl/6hsGwa for a summary. Bug: 32022681 Test: This is the test suite. Change-Id: Ib200b68e0c7844df02eb9f086385d6c36e306d45 --- keymaster/3.0/default/KeymasterDevice.cpp | 19 +- keymaster/3.0/vts/functional/Android.mk | 37 + .../3.0/vts/functional/attestation_record.cpp | 289 ++ keymaster/3.0/vts/functional/attestation_record.h | 57 + keymaster/3.0/vts/functional/authorization_set.cpp | 422 +++ keymaster/3.0/vts/functional/authorization_set.h | 420 +++ keymaster/3.0/vts/functional/key_param_output.cpp | 131 + keymaster/3.0/vts/functional/key_param_output.h | 50 + .../3.0/vts/functional/keymaster_hidl_hal_test.cpp | 3909 ++++++++++++++++++++ keymaster/3.0/vts/functional/keymaster_tags.h | 450 +++ .../3.0/vts/functional/keystore_tags_utils.cpp | 50 + keymaster/3.0/vts/functional/openssl_utils.h | 52 + 12 files changed, 5880 insertions(+), 6 deletions(-) create mode 100644 keymaster/3.0/vts/functional/Android.mk create mode 100644 keymaster/3.0/vts/functional/attestation_record.cpp create mode 100644 keymaster/3.0/vts/functional/attestation_record.h create mode 100644 keymaster/3.0/vts/functional/authorization_set.cpp create mode 100644 keymaster/3.0/vts/functional/authorization_set.h create mode 100644 keymaster/3.0/vts/functional/key_param_output.cpp create mode 100644 keymaster/3.0/vts/functional/key_param_output.h create mode 100644 keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp create mode 100644 keymaster/3.0/vts/functional/keymaster_tags.h create mode 100644 keymaster/3.0/vts/functional/keystore_tags_utils.cpp create mode 100644 keymaster/3.0/vts/functional/openssl_utils.h diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp index 720b9460..6b4524b4 100644 --- a/keymaster/3.0/default/KeymasterDevice.cpp +++ b/keymaster/3.0/default/KeymasterDevice.cpp @@ -698,14 +698,21 @@ Return KeymasterDevice::abort(uint64_t operationHandle) { return legacy_enum_conversion(keymaster_device_->abort(keymaster_device_, operationHandle)); } -IKeymasterDevice* HIDL_FETCH_IKeymasterDevice(const char* /* name */) { +IKeymasterDevice* HIDL_FETCH_IKeymasterDevice(const char* name) { keymaster2_device_t* dev = nullptr; - uint32_t version; - bool supports_ec; - bool supports_all_digests; - auto rc = keymaster_device_initialize(&dev, &version, &supports_ec, &supports_all_digests); - if (rc) return nullptr; + ALOGI("Fetching keymaster device name %s", name); + + uint32_t version = -1; + bool supports_ec = false; + bool supports_all_digests = false; + + if (name && strcmp(name, "softwareonly") == 0) { + dev = (new SoftKeymasterDevice(new SoftwareOnlyHidlKeymasterContext))->keymaster2_device(); + } else if (name && strcmp(name, "default") == 0) { + auto rc = keymaster_device_initialize(&dev, &version, &supports_ec, &supports_all_digests); + if (rc) return nullptr; + } auto kmrc = ::keymaster::ConfigureDevice(dev); if (kmrc != KM_ERROR_OK) { diff --git a/keymaster/3.0/vts/functional/Android.mk b/keymaster/3.0/vts/functional/Android.mk new file mode 100644 index 00000000..1b29cde2 --- /dev/null +++ b/keymaster/3.0/vts/functional/Android.mk @@ -0,0 +1,37 @@ +# Copyright (C) 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := keymaster_hidl_hal_test +LOCAL_SRC_FILES := \ + authorization_set.cpp \ + attestation_record.cpp \ + key_param_output.cpp \ + keymaster_hidl_hal_test.cpp \ + keystore_tags_utils.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + android.hardware.keymaster@3.0 \ + libcrypto \ + libhidlbase \ + liblog \ + libsoftkeymasterdevice \ + libutils \ + +LOCAL_STATIC_LIBRARIES := \ + VtsHalHidlTargetTestBase \ + +include $(BUILD_NATIVE_TEST) diff --git a/keymaster/3.0/vts/functional/attestation_record.cpp b/keymaster/3.0/vts/functional/attestation_record.cpp new file mode 100644 index 00000000..6cdd44c0 --- /dev/null +++ b/keymaster/3.0/vts/functional/attestation_record.cpp @@ -0,0 +1,289 @@ +/* + * Copyright 2016 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 "attestation_record.h" + +#include + +#include +#include +#include +#include + +#include "openssl_utils.h" + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +struct stack_st_ASN1_TYPE_Delete { + void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); } +}; + +struct ASN1_STRING_Delete { + void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); } +}; + +struct ASN1_TYPE_Delete { + void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); } +}; + +#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER) + +typedef struct km_root_of_trust { + ASN1_OCTET_STRING* verified_boot_key; + ASN1_BOOLEAN* device_locked; + ASN1_ENUMERATED* verified_boot_state; +} KM_ROOT_OF_TRUST; + +ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = { + ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING), + ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN), + ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED), +} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST); +IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST); + +typedef struct km_auth_list { + ASN1_INTEGER_SET* purpose; + ASN1_INTEGER* algorithm; + ASN1_INTEGER* key_size; + ASN1_INTEGER_SET* digest; + ASN1_INTEGER_SET* padding; + ASN1_INTEGER_SET* kdf; + ASN1_INTEGER* ec_curve; + ASN1_INTEGER* rsa_public_exponent; + ASN1_INTEGER* active_date_time; + ASN1_INTEGER* origination_expire_date_time; + ASN1_INTEGER* usage_expire_date_time; + ASN1_NULL* no_auth_required; + ASN1_INTEGER* user_auth_type; + ASN1_INTEGER* auth_timeout; + ASN1_NULL* allow_while_on_body; + ASN1_NULL* all_applications; + ASN1_OCTET_STRING* application_id; + ASN1_INTEGER* creation_date_time; + ASN1_INTEGER* origin; + ASN1_NULL* rollback_resistant; + KM_ROOT_OF_TRUST* root_of_trust; + ASN1_INTEGER* os_version; + ASN1_INTEGER* os_patchlevel; + ASN1_OCTET_STRING* attestation_application_id; +} KM_AUTH_LIST; + +ASN1_SEQUENCE(KM_AUTH_LIST) = { + ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()), + ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()), + ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()), + ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, kdf, ASN1_INTEGER, TAG_KDF.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER, + TAG_RSA_PUBLIC_EXPONENT.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER, + TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER, + TAG_USAGE_EXPIRE_DATETIME.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL, TAG_ALLOW_WHILE_ON_BODY.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, all_applications, ASN1_NULL, TAG_ALL_APPLICATIONS.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, application_id, ASN1_OCTET_STRING, TAG_APPLICATION_ID.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER, TAG_CREATION_DATETIME.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistant, ASN1_NULL, TAG_ROLLBACK_RESISTANT.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()), + ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING, + TAG_ATTESTATION_APPLICATION_ID.maskedTag()), +} ASN1_SEQUENCE_END(KM_AUTH_LIST); +IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST); + +typedef struct km_key_description { + ASN1_INTEGER* attestation_version; + ASN1_ENUMERATED* attestation_security_level; + ASN1_INTEGER* keymaster_version; + ASN1_ENUMERATED* keymaster_security_level; + ASN1_OCTET_STRING* attestation_challenge; + KM_AUTH_LIST* software_enforced; + KM_AUTH_LIST* tee_enforced; + ASN1_INTEGER* unique_id; +} KM_KEY_DESCRIPTION; + +ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = { + ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_version, ASN1_INTEGER), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_security_level, ASN1_ENUMERATED), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST), +} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION); +IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION); + +template +void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag ttag, + AuthorizationSet* auth_list) { + typedef typename TypedTag2ValueType::type ValueT; + for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) { + auth_list->push_back( + ttag, static_cast(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i)))); + } +} + +template +void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag ttag, + AuthorizationSet* auth_list) { + typedef typename TypedTag2ValueType::type ValueT; + if (!asn1_int) return; + auth_list->push_back(ttag, static_cast(ASN1_INTEGER_get(asn1_int))); +} + +template +void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag ttag, + AuthorizationSet* auth_list) { + if (!asn1_int) return; + auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int)); +} + +BIGNUM* construct_uint_max() { + BIGNUM* value = BN_new(); + BIGNUM_Ptr one(BN_new()); + BN_one(one.get()); + BN_lshift(value, one.get(), 32); + return value; +} + +uint64_t BignumToUint64(BIGNUM* num) { + static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)), + "This implementation only supports 32 and 64-bit BN_ULONG"); + if (sizeof(BN_ULONG) == sizeof(uint32_t)) { + BIGNUM_Ptr uint_max(construct_uint_max()); + BIGNUM_Ptr hi(BN_new()), lo(BN_new()); + BN_CTX_Ptr ctx(BN_CTX_new()); + BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get()); + return static_cast(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get()); + } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) { + return BN_get_word(num); + } else { + return 0; + } +} + +template +void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag ttag, + AuthorizationSet* auth_list) { + if (!asn1_int) return; + BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr)); + auth_list->push_back(ttag, BignumToUint64(num.get())); +} + +template +void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag ttag, + AuthorizationSet* auth_list) { + if (!asn1_int) return; + BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr)); + auth_list->push_back(ttag, BignumToUint64(num.get())); +} + +template +void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag ttag, + AuthorizationSet* auth_list) { + if (!asn1_null) return; + auth_list->push_back(ttag); +} + +template +void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag ttag, + AuthorizationSet* auth_list) { + if (!asn1_string) return; + hidl_vec buf; + buf.setToExternal(asn1_string->data, asn1_string->length); + auth_list->push_back(ttag, buf); +} + +// Extract the values from the specified ASN.1 record and place them in auth_list. +static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) { + if (!record) return ErrorCode::OK; + + copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list); + copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list); + copyAuthTag(record->all_applications, TAG_ALL_APPLICATIONS, auth_list); + copyAuthTag(record->application_id, TAG_APPLICATION_ID, auth_list); + copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list); + copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list); + copyAuthTag(record->digest, TAG_DIGEST, auth_list); + copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list); + copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list); + copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list); + copyAuthTag(record->origin, TAG_ORIGIN, auth_list); + copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list); + copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list); + copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list); + copyAuthTag(record->padding, TAG_PADDING, auth_list); + copyAuthTag(record->purpose, TAG_PURPOSE, auth_list); + copyAuthTag(record->rollback_resistant, TAG_ROLLBACK_RESISTANT, auth_list); + copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list); + copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list); + copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list); + + return ErrorCode::OK; +} + +MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION) + +// Parse the DER-encoded attestation record, placing the results in keymaster_version, +// attestation_challenge, software_enforced, tee_enforced and unique_id. +ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len, + uint32_t* attestation_version, // + SecurityLevel* attestation_security_level, + uint32_t* keymaster_version, + SecurityLevel* keymaster_security_level, + hidl_vec* attestation_challenge, + AuthorizationSet* software_enforced, + AuthorizationSet* tee_enforced, // + hidl_vec* unique_id) { + const uint8_t* p = asn1_key_desc; + KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len)); + if (!record.get()) return ErrorCode::UNKNOWN_ERROR; + + *attestation_version = ASN1_INTEGER_get(record->attestation_version); + *attestation_security_level = + static_cast(ASN1_ENUMERATED_get(record->attestation_security_level)); + *keymaster_version = ASN1_INTEGER_get(record->keymaster_version); + *keymaster_security_level = + static_cast(ASN1_ENUMERATED_get(record->keymaster_security_level)); + + attestation_challenge->setToExternal(record->attestation_challenge->data, + record->attestation_challenge->length); + + unique_id->setToExternal(record->unique_id->data, record->unique_id->length); + + ErrorCode error = extract_auth_list(record->software_enforced, software_enforced); + if (error != ErrorCode::OK) return error; + + return extract_auth_list(record->tee_enforced, tee_enforced); +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android diff --git a/keymaster/3.0/vts/functional/attestation_record.h b/keymaster/3.0/vts/functional/attestation_record.h new file mode 100644 index 00000000..a042055d --- /dev/null +++ b/keymaster/3.0/vts/functional/attestation_record.h @@ -0,0 +1,57 @@ +/* + * Copyright 2016 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 HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_ +#define HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_ + +#include "authorization_set.h" + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +/** + * The OID for Android attestation records. For the curious, it breaks down as follows: + * + * 1 = ISO + * 3 = org + * 6 = DoD (Huh? OIDs are weird.) + * 1 = IANA + * 4 = Private + * 1 = Enterprises + * 11129 = Google + * 2 = Google security + * 1 = certificate extension + * 17 = Android attestation extension. + */ +static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17"; + +ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len, + uint32_t* attestation_version, // + SecurityLevel* attestation_security_level, + uint32_t* keymaster_version, + SecurityLevel* keymaster_security_level, + hidl_vec* attestation_challenge, + AuthorizationSet* software_enforced, + AuthorizationSet* tee_enforced, // + hidl_vec* unique_id); +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android + +#endif // HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_ diff --git a/keymaster/3.0/vts/functional/authorization_set.cpp b/keymaster/3.0/vts/functional/authorization_set.cpp new file mode 100644 index 00000000..303f7e7c --- /dev/null +++ b/keymaster/3.0/vts/functional/authorization_set.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2014 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 "authorization_set.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +inline bool keyParamLess(const KeyParameter& a, const KeyParameter& b) { + if (a.tag != b.tag) return a.tag < b.tag; + int retval; + switch (typeFromTag(a.tag)) { + case TagType::INVALID: + case TagType::BOOL: + return false; + case TagType::ENUM: + case TagType::ENUM_REP: + case TagType::UINT: + case TagType::UINT_REP: + return a.f.integer < b.f.integer; + case TagType::ULONG: + case TagType::ULONG_REP: + return a.f.longInteger < b.f.longInteger; + case TagType::DATE: + return a.f.dateTime < b.f.dateTime; + case TagType::BIGNUM: + case TagType::BYTES: + // Handle the empty cases. + if (a.blob.size() == 0) return b.blob.size() != 0; + if (b.blob.size() == 0) return false; + + retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size())); + if (retval == 0) { + // One is the prefix of the other, so the longer wins + return a.blob.size() < b.blob.size(); + } else { + return retval < 0; + } + } + return false; +} + +inline bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) { + if (a.tag != b.tag) return false; + + switch (typeFromTag(a.tag)) { + case TagType::INVALID: + case TagType::BOOL: + return true; + case TagType::ENUM: + case TagType::ENUM_REP: + case TagType::UINT: + case TagType::UINT_REP: + return a.f.integer == b.f.integer; + case TagType::ULONG: + case TagType::ULONG_REP: + return a.f.longInteger == b.f.longInteger; + case TagType::DATE: + return a.f.dateTime == b.f.dateTime; + case TagType::BIGNUM: + case TagType::BYTES: + if (a.blob.size() != b.blob.size()) return false; + return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0; + } + return false; +} + +void AuthorizationSet::Sort() { + std::sort(data_.begin(), data_.end(), keyParamLess); +} + +void AuthorizationSet::Deduplicate() { + if (data_.empty()) return; + + Sort(); + std::vector result; + + auto curr = data_.begin(); + auto prev = curr++; + for (; curr != data_.end(); ++prev, ++curr) { + if (prev->tag == Tag::INVALID) continue; + + if (!keyParamEqual(*prev, *curr)) { + result.emplace_back(std::move(*prev)); + } + } + result.emplace_back(std::move(*prev)); + + std::swap(data_, result); +} + +void AuthorizationSet::Union(const AuthorizationSet& other) { + data_.insert(data_.end(), other.data_.begin(), other.data_.end()); + Deduplicate(); +} + +void AuthorizationSet::Subtract(const AuthorizationSet& other) { + Deduplicate(); + + auto i = other.begin(); + while (i != other.end()) { + int pos = -1; + do { + pos = find(i->tag, pos); + if (pos != -1 && keyParamEqual(*i, data_[pos])) { + data_.erase(data_.begin() + pos); + break; + } + } while (pos != -1); + ++i; + } +} + +int AuthorizationSet::find(Tag tag, int begin) const { + auto iter = data_.begin() + (1 + begin); + + while (iter != data_.end() && iter->tag != tag) + ++iter; + + if (iter != data_.end()) return iter - data_.begin(); + return -1; +} + +bool AuthorizationSet::erase(int index) { + auto pos = data_.begin() + index; + if (pos != data_.end()) { + data_.erase(pos); + return true; + } + return false; +} + +KeyParameter& AuthorizationSet::operator[](int at) { + return data_[at]; +} + +const KeyParameter& AuthorizationSet::operator[](int at) const { + return data_[at]; +} + +void AuthorizationSet::Clear() { + data_.clear(); +} + +size_t AuthorizationSet::GetTagCount(Tag tag) const { + size_t count = 0; + for (int pos = -1; (pos = find(tag, pos)) != -1;) + ++count; + return count; +} + +NullOr AuthorizationSet::GetEntry(Tag tag) const { + int pos = find(tag); + if (pos == -1) return {}; + return data_[pos]; +} + +/** + * Persistent format is: + * | 32 bit indirect_size | + * -------------------------------- + * | indirect_size bytes of data | this is where the blob data is stored + * -------------------------------- + * | 32 bit element_count | number of entries + * | 32 bit elements_size | total bytes used by entries (entries have variable length) + * -------------------------------- + * | elementes_size bytes of data | where the elements are stored + */ + +/** + * Persistent format of blobs and bignums: + * | 32 bit tag | + * | 32 bit blob_length | + * | 32 bit indirect_offset | + */ + +struct OutStreams { + std::ostream& indirect; + std::ostream& elements; +}; + +OutStreams& serializeParamValue(OutStreams& out, const hidl_vec& blob) { + uint32_t buffer; + + // write blob_length + auto blob_length = blob.size(); + if (blob_length > std::numeric_limits::max()) { + out.elements.setstate(std::ios_base::badbit); + return out; + } + buffer = blob_length; + out.elements.write(reinterpret_cast(&buffer), sizeof(uint32_t)); + + // write indirect_offset + auto offset = out.indirect.tellp(); + if (offset < 0 || offset > std::numeric_limits::max() || + static_cast((std::numeric_limits::max() - offset)) < + blob_length) { // overflow check + out.elements.setstate(std::ios_base::badbit); + return out; + } + buffer = offset; + out.elements.write(reinterpret_cast(&buffer), sizeof(uint32_t)); + + // write blob to indirect stream + if (blob_length) out.indirect.write(reinterpret_cast(&blob[0]), blob_length); + + return out; +} + +template OutStreams& serializeParamValue(OutStreams& out, const T& value) { + out.elements.write(reinterpret_cast(&value), sizeof(T)); + return out; +} + +OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) { + // skip invalid entries. + return out; +} +template OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) { + out.elements.write(reinterpret_cast(¶m.tag), sizeof(int32_t)); + return serializeParamValue(out, accessTagValue(ttag, param)); +} + +template struct choose_serializer; +template struct choose_serializer> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer::serialize(out, param); + } +}; +template <> struct choose_serializer<> { + static OutStreams& serialize(OutStreams& out, const KeyParameter&) { return out; } +}; +template +struct choose_serializer, Tail...> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + if (param.tag == tag) { + return V3_0::serialize(TypedTag(), out, param); + } else { + return choose_serializer::serialize(out, param); + } + } +}; + +OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer::serialize(out, param); +} + +std::ostream& serialize(std::ostream& out, const std::vector& params) { + std::stringstream indirect; + std::stringstream elements; + OutStreams streams = {indirect, elements}; + for (const auto& param : params) { + serialize(streams, param); + } + if (indirect.bad() || elements.bad()) { + out.setstate(std::ios_base::badbit); + return out; + } + auto pos = indirect.tellp(); + if (pos < 0 || pos > std::numeric_limits::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t indirect_size = pos; + pos = elements.tellp(); + if (pos < 0 || pos > std::numeric_limits::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t elements_size = pos; + uint32_t element_count = params.size(); + + out.write(reinterpret_cast(&indirect_size), sizeof(uint32_t)); + + pos = out.tellp(); + if (indirect_size) out << indirect.rdbuf(); + assert(out.tellp() - pos == indirect_size); + + out.write(reinterpret_cast(&element_count), sizeof(uint32_t)); + out.write(reinterpret_cast(&elements_size), sizeof(uint32_t)); + + pos = out.tellp(); + if (elements_size) out << elements.rdbuf(); + assert(out.tellp() - pos == elements_size); + + return out; +} + +struct InStreams { + std::istream& indirect; + std::istream& elements; +}; + +InStreams& deserializeParamValue(InStreams& in, hidl_vec* blob) { + uint32_t blob_length = 0; + uint32_t offset = 0; + in.elements.read(reinterpret_cast(&blob_length), sizeof(uint32_t)); + blob->resize(blob_length); + in.elements.read(reinterpret_cast(&offset), sizeof(uint32_t)); + in.indirect.seekg(offset); + in.indirect.read(reinterpret_cast(&(*blob)[0]), blob->size()); + return in; +} + +template InStreams& deserializeParamValue(InStreams& in, T* value) { + in.elements.read(reinterpret_cast(value), sizeof(T)); + return in; +} + +InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) { + // there should be no invalid KeyParamaters but if handle them as zero sized. + return in; +} + +template InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) { + return deserializeParamValue(in, &accessTagValue(ttag, *param)); +} + +template struct choose_deserializer; +template struct choose_deserializer> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + return choose_deserializer::deserialize(in, param); + } +}; +template <> struct choose_deserializer<> { + static InStreams& deserialize(InStreams& in, KeyParameter*) { + // encountered an unknown tag -> fail parsing + in.elements.setstate(std::ios_base::badbit); + return in; + } +}; +template +struct choose_deserializer, Tail...> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + if (param->tag == tag) { + return V3_0::deserialize(TypedTag(), in, param); + } else { + return choose_deserializer::deserialize(in, param); + } + } +}; + +InStreams& deserialize(InStreams& in, KeyParameter* param) { + in.elements.read(reinterpret_cast(¶m->tag), sizeof(Tag)); + return choose_deserializer::deserialize(in, param); +} + +std::istream& deserialize(std::istream& in, std::vector* params) { + uint32_t indirect_size = 0; + in.read(reinterpret_cast(&indirect_size), sizeof(uint32_t)); + std::string indirect_buffer(indirect_size, '\0'); + if (indirect_buffer.size() != indirect_size) { + in.setstate(std::ios_base::badbit); + return in; + } + in.read(&indirect_buffer[0], indirect_buffer.size()); + + uint32_t element_count = 0; + in.read(reinterpret_cast(&element_count), sizeof(uint32_t)); + uint32_t elements_size = 0; + in.read(reinterpret_cast(&elements_size), sizeof(uint32_t)); + + std::string elements_buffer(elements_size, '\0'); + if (elements_buffer.size() != elements_size) { + in.setstate(std::ios_base::badbit); + return in; + } + in.read(&elements_buffer[0], elements_buffer.size()); + + if (in.bad()) return in; + + // TODO write one-shot stream buffer to avoid copying here + std::stringstream indirect(indirect_buffer); + std::stringstream elements(elements_buffer); + InStreams streams = {indirect, elements}; + + params->resize(element_count); + + for (uint32_t i = 0; i < element_count; ++i) { + deserialize(streams, &(*params)[i]); + } + return in; +} + +void AuthorizationSet::Serialize(std::ostream* out) const { + serialize(*out, data_); +} + +void AuthorizationSet::Deserialize(std::istream* in) { + deserialize(*in, &data_); +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android diff --git a/keymaster/3.0/vts/functional/authorization_set.h b/keymaster/3.0/vts/functional/authorization_set.h new file mode 100644 index 00000000..5f92d816 --- /dev/null +++ b/keymaster/3.0/vts/functional/authorization_set.h @@ -0,0 +1,420 @@ +/* + * 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 HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_ +#define HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_ + +#include "keymaster_tags.h" + +#include +#include + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +class AuthorizationSetBuilder; + +/** + * An ordered collection of KeyParameters. It provides memory ownership and some convenient + * functionality for sorting, deduplicating, joining, and subtracting sets of KeyParameters. + * For serialization, wrap the backing store of this structure in a hidl_vec. + */ +class AuthorizationSet { + public: + typedef KeyParameter value_type; + + /** + * Construct an empty, dynamically-allocated, growable AuthorizationSet. + */ + AuthorizationSet(){}; + + // Copy constructor. + AuthorizationSet(const AuthorizationSet& other) : data_(other.data_) {} + + // Move constructor. + AuthorizationSet(AuthorizationSet&& other) : data_(std::move(other.data_)) {} + + // Constructor from hidl_vec + AuthorizationSet(const hidl_vec& other) { *this = other; } + + // Copy assignment. + AuthorizationSet& operator=(const AuthorizationSet& other) { + data_ = other.data_; + return *this; + } + + // Move assignment. + AuthorizationSet& operator=(AuthorizationSet&& other) { + data_ = std::move(other.data_); + return *this; + } + + AuthorizationSet& operator=(const hidl_vec& other) { + if (other.size() > 0) { + data_.resize(other.size()); + for (size_t i = 0; i < data_.size(); ++i) { + /* This makes a deep copy even of embedded blobs. + * See assignment operator/copy constructor of hidl_vec.*/ + data_[i] = other[i]; + } + } + return *this; + } + + /** + * Clear existing authorization set data + */ + void Clear(); + + ~AuthorizationSet() = default; + + /** + * Returns the size of the set. + */ + size_t size() const { return data_.size(); } + + /** + * Returns true if the set is empty. + */ + bool empty() const { return size() == 0; } + + /** + * Returns the data in the set, directly. Be careful with this. + */ + const KeyParameter* data() const { return data_.data(); } + + /** + * Sorts the set + */ + void Sort(); + + /** + * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the + * AuthorizationSetBuilder). + */ + void Deduplicate(); + + /** + * Adds all elements from \p set that are not already present in this AuthorizationSet. As a + * side-effect, if \p set is not null this AuthorizationSet will end up sorted. + */ + void Union(const AuthorizationSet& set); + + /** + * Removes all elements in \p set from this AuthorizationSet. + */ + void Subtract(const AuthorizationSet& set); + + /** + * Returns the offset of the next entry that matches \p tag, starting from the element after \p + * begin. If not found, returns -1. + */ + int find(Tag tag, int begin = -1) const; + + /** + * Removes the entry at the specified index. Returns true if successful, false if the index was + * out of bounds. + */ + bool erase(int index); + + /** + * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration + */ + std::vector::const_iterator begin() const { return data_.begin(); } + + /** + * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration + */ + std::vector::const_iterator end() const { return data_.end(); } + + /** + * Returns the nth element of the set. + * Like for std::vector::operator[] there is no range check performed. Use of out of range + * indices is undefined. + */ + KeyParameter& operator[](int n); + + /** + * Returns the nth element of the set. + * Like for std::vector::operator[] there is no range check performed. Use of out of range + * indices is undefined. + */ + const KeyParameter& operator[](int n) const; + + /** + * Returns true if the set contains at least one instance of \p tag + */ + bool Contains(Tag tag) const { return find(tag) != -1; } + + template bool Contains(T tag) const { return find(tag) != -1; } + + template + bool Contains(TypedTag ttag, const ValueT& value) const { + for (const auto& param : data_) { + auto entry = authorizationValue(ttag, param); + if (entry.isOk() && static_cast(entry.value()) == value) return true; + } + return false; + } + /** + * Returns the number of \p tag entries. + */ + size_t GetTagCount(Tag tag) const; + + template + inline NullOr::type&> GetTagValue(T tag) const { + auto entry = GetEntry(tag); + if (entry.isOk()) return authorizationValue(tag, entry.value()); + return {}; + } + + void push_back(const KeyParameter& param) { data_.push_back(param); } + void push_back(KeyParameter&& param) { data_.push_back(std::move(param)); } + + void push_back(const AuthorizationSet& set) { + for (auto& entry : set) { + push_back(entry); + } + } + + void push_back(AuthorizationSet&& set) { + move(set.begin(), set.end()); + set.Clear(); + } + + template + void push_back(TypedTag ttag, const uint8_t* data, size_t data_length) { + hidl_vec new_blob; + new_blob.setToExternal(const_cast(data), data_length); + push_back(ttag, std::move(new_blob)); + } + + /** + * Append the tag and enumerated value to the set. + * "val" may be exactly one parameter unless a boolean parameter is added. + * In this case "val" is omitted. This condition is checked at compile time by Authorization() + */ + template void push_back(TypedTagT tag, Value&&... val) { + push_back(Authorization(tag, std::forward(val)...)); + } + + template void push_back(Iterator begin, Iterator end) { + while (begin != end) { + push_back(*begin); + ++begin; + } + } + + template void move(Iterator begin, Iterator end) { + std::move(begin, end, std::back_inserter(data_)); + } + + hidl_vec hidl_data() const { + hidl_vec result; + result.setToExternal(const_cast(data()), size()); + return result; + } + + void Serialize(std::ostream* out) const; + void Deserialize(std::istream* in); + + private: + NullOr GetEntry(Tag tag) const; + + std::vector data_; +}; + +class AuthorizationSetBuilder : public AuthorizationSet { + public: + template + AuthorizationSetBuilder& Authorization(TagType ttag, ValueType&&... value) { + push_back(ttag, std::forward(value)...); + return *this; + } + + template + AuthorizationSetBuilder& Authorization(TypedTag ttag, const uint8_t* data, + size_t data_length) { + hidl_vec new_blob; + new_blob.setToExternal(const_cast(data), data_length); + push_back(ttag, std::move(new_blob)); + return *this; + } + + template + AuthorizationSetBuilder& Authorization(TypedTag ttag, const char* data, + size_t data_length) { + return Authorization(ttag, reinterpret_cast(data), data_length); + } + + AuthorizationSetBuilder& Authorizations(AuthorizationSet&& set); + AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set); + + AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& EcdsaKey(uint32_t key_size); + AuthorizationSetBuilder& EcdsaKey(EcCurve curve); + AuthorizationSetBuilder& AesKey(uint32_t key_size); + AuthorizationSetBuilder& HmacKey(uint32_t key_size); + + AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size); + AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve); + AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size); + + AuthorizationSetBuilder& SigningKey(); + AuthorizationSetBuilder& EncryptionKey(); + AuthorizationSetBuilder& NoDigestOrPadding(); + AuthorizationSetBuilder& EcbMode(); + + AuthorizationSetBuilder& BlockMode(std::initializer_list block_modes); + AuthorizationSetBuilder& Digest(std::initializer_list digests); + AuthorizationSetBuilder& Padding(std::initializer_list padding_modes); + + // The following forwarding templates enable BlockMode,Digest and Padding to be called with a + // variable number of arguments; no need to wrap them in braces to make them an initalizer_list. + template AuthorizationSetBuilder& BlockMode(T&&... a) { + return BlockMode({std::forward(a)...}); + } + template AuthorizationSetBuilder& Digest(T&&... a) { + return Digest({std::forward(a)...}); + } + template AuthorizationSetBuilder& Padding(T&&... a) { + return Padding({std::forward(a)...}); + } +}; + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::Authorizations(AuthorizationSet&& set) { + move(set.begin(), set.end()); + set.Clear(); + return *this; +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::Authorizations(const AuthorizationSet& set) { + push_back(set.begin(), set.end()); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size, + uint64_t public_exponent) { + Authorization(TAG_ALGORITHM, Algorithm::RSA); + Authorization(TAG_KEY_SIZE, key_size); + Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::EC); + Authorization(TAG_KEY_SIZE, key_size); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) { + Authorization(TAG_ALGORITHM, Algorithm::EC); + Authorization(TAG_EC_CURVE, curve); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::AES); + return Authorization(TAG_KEY_SIZE, key_size); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::HMAC); + Authorization(TAG_KEY_SIZE, key_size); + return SigningKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size, + uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return SigningKey(); +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return EncryptionKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) { + EcdsaKey(key_size); + return SigningKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) { + EcdsaKey(curve); + return SigningKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) { + AesKey(key_size); + return EncryptionKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() { + Authorization(TAG_PURPOSE, KeyPurpose::SIGN); + return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() { + Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT); + return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() { + Authorization(TAG_DIGEST, Digest::NONE); + return Authorization(TAG_PADDING, PaddingMode::NONE); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() { + return BlockMode(BlockMode::ECB); +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::BlockMode(std::initializer_list block_modes) { + for (auto block_mode : block_modes) { + Authorization(TAG_BLOCK_MODE, block_mode); + } + return *this; +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::Digest(std::initializer_list digests) { + for (auto digest : digests) { + Authorization(TAG_DIGEST, digest); + } + return *this; +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::Padding(std::initializer_list padding_modes) { + for (auto padding : padding_modes) { + Authorization(TAG_PADDING, padding); + } + return *this; +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android + +#endif // HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_ diff --git a/keymaster/3.0/vts/functional/key_param_output.cpp b/keymaster/3.0/vts/functional/key_param_output.cpp new file mode 100644 index 00000000..fc9f6852 --- /dev/null +++ b/keymaster/3.0/vts/functional/key_param_output.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 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 "key_param_output.h" + +#include + +namespace android { +namespace hardware { + +namespace keymaster { +namespace V3_0 { + +::std::ostream& operator<<(::std::ostream& os, const hidl_vec& set) { + if (set.size() == 0) { + os << "(Empty)" << ::std::endl; + } else { + os << "\n"; + for (size_t i = 0; i < set.size(); ++i) + os << set[i] << ::std::endl; + } + return os; +} + +::std::ostream& operator<<(::std::ostream& os, ErrorCode value) { + return os << (int)value; +} + +::std::ostream& operator<<(::std::ostream& os, Digest value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, Algorithm value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, BlockMode value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, PaddingMode value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, KeyOrigin value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, KeyPurpose value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, EcCurve value) { + return os << stringify(value); +} + +::std::ostream& operator<<(::std::ostream& os, const KeyParameter& param) { + os << stringifyTag(param.tag) << ": "; + switch (typeFromTag(param.tag)) { + case TagType::INVALID: + return os << " Invalid"; + case TagType::UINT_REP: + case TagType::UINT: + return os << param.f.integer; + case TagType::ENUM_REP: + case TagType::ENUM: + switch (param.tag) { + case Tag::ALGORITHM: + return os << param.f.algorithm; + case Tag::BLOCK_MODE: + return os << param.f.blockMode; + case Tag::PADDING: + return os << param.f.paddingMode; + case Tag::DIGEST: + return os << param.f.digest; + case Tag::EC_CURVE: + return os << (int)param.f.ecCurve; + case Tag::ORIGIN: + return os << param.f.origin; + case Tag::BLOB_USAGE_REQUIREMENTS: + return os << (int)param.f.keyBlobUsageRequirements; + case Tag::PURPOSE: + return os << param.f.purpose; + default: + return os << " UNKNOWN ENUM " << param.f.integer; + } + case TagType::ULONG_REP: + case TagType::ULONG: + return os << param.f.longInteger; + case TagType::DATE: + return os << param.f.dateTime; + case TagType::BOOL: + return os << "true"; + case TagType::BIGNUM: + os << " Bignum: "; + for (size_t i = 0; i < param.blob.size(); ++i) { + os << ::std::hex << ::std::setw(2) << static_cast(param.blob[i]) << ::std::dec; + } + return os; + case TagType::BYTES: + os << " Bytes: "; + for (size_t i = 0; i < param.blob.size(); ++i) { + os << ::std::hex << ::std::setw(2) << static_cast(param.blob[i]) << ::std::dec; + } + return os; + } + return os << "UNKNOWN TAG TYPE!"; +} + +::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& chars) { + return os << "SW: " << chars.softwareEnforced << ::std::endl + << "TEE: " << chars.teeEnforced << ::std::endl; +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android diff --git a/keymaster/3.0/vts/functional/key_param_output.h b/keymaster/3.0/vts/functional/key_param_output.h new file mode 100644 index 00000000..5edec2d6 --- /dev/null +++ b/keymaster/3.0/vts/functional/key_param_output.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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 + +#include + +#include "keymaster_tags.h" + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +template +::std::ostream& operator<<(::std::ostream& os, const NullOr& value) { + if (!value.isOk()) { + os << "(value not present)"; + } else { + os << value.value(); + } + return os; +} + +::std::ostream& operator<<(::std::ostream& os, const hidl_vec& set); +::std::ostream& operator<<(::std::ostream& os, BlockMode value); +::std::ostream& operator<<(::std::ostream& os, Digest value); +::std::ostream& operator<<(::std::ostream& os, EcCurve value); +::std::ostream& operator<<(::std::ostream& os, ErrorCode value); +::std::ostream& operator<<(::std::ostream& os, PaddingMode value); +::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& value); +::std::ostream& operator<<(::std::ostream& os, const KeyParameter& value); + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android diff --git a/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp new file mode 100644 index 00000000..92266b55 --- /dev/null +++ b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp @@ -0,0 +1,3909 @@ +/* + * Copyright (C) 2016 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. + */ + +#define LOG_TAG "keymaster_hidl_hal_test" +#include + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include "authorization_set.h" +#include "key_param_output.h" + +#include + +#include "attestation_record.h" +#include "openssl_utils.h" + +using ::android::sp; + +using ::std::string; + +// This service_name will be passed to getService when retrieving the keymaster service to test. To +// change it from "default" specify the selected service name on the command line. The first +// non-gtest argument will be used as the service name. +string service_name = "default"; + +namespace android { +namespace hardware { + +template bool operator==(const hidl_vec& a, const hidl_vec& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != b[i]) { + return false; + } + } + return true; +} + +namespace keymaster { +namespace V3_0 { + +bool operator==(const KeyParameter& a, const KeyParameter& b) { + if (a.tag != b.tag) { + return false; + } + + switch (a.tag) { + + /* Boolean tags */ + case Tag::INVALID: + case Tag::CALLER_NONCE: + case Tag::INCLUDE_UNIQUE_ID: + case Tag::ECIES_SINGLE_HASH_MODE: + case Tag::BOOTLOADER_ONLY: + case Tag::NO_AUTH_REQUIRED: + case Tag::ALLOW_WHILE_ON_BODY: + case Tag::EXPORTABLE: + case Tag::ALL_APPLICATIONS: + case Tag::ROLLBACK_RESISTANT: + case Tag::RESET_SINCE_ID_ROTATION: + return true; + + /* Integer tags */ + case Tag::KEY_SIZE: + case Tag::MIN_MAC_LENGTH: + case Tag::MIN_SECONDS_BETWEEN_OPS: + case Tag::MAX_USES_PER_BOOT: + case Tag::ALL_USERS: + case Tag::USER_ID: + case Tag::OS_VERSION: + case Tag::OS_PATCHLEVEL: + case Tag::MAC_LENGTH: + case Tag::AUTH_TIMEOUT: + return a.f.integer == b.f.integer; + + /* Long integer tags */ + case Tag::RSA_PUBLIC_EXPONENT: + case Tag::USER_SECURE_ID: + return a.f.longInteger == b.f.longInteger; + + /* Date-time tags */ + case Tag::ACTIVE_DATETIME: + case Tag::ORIGINATION_EXPIRE_DATETIME: + case Tag::USAGE_EXPIRE_DATETIME: + case Tag::CREATION_DATETIME: + return a.f.dateTime == b.f.dateTime; + + /* Bytes tags */ + case Tag::APPLICATION_ID: + case Tag::APPLICATION_DATA: + case Tag::ROOT_OF_TRUST: + case Tag::UNIQUE_ID: + case Tag::ATTESTATION_CHALLENGE: + case Tag::ATTESTATION_APPLICATION_ID: + case Tag::ATTESTATION_ID_BRAND: + case Tag::ATTESTATION_ID_DEVICE: + case Tag::ATTESTATION_ID_PRODUCT: + case Tag::ATTESTATION_ID_SERIAL: + case Tag::ATTESTATION_ID_IMEI: + case Tag::ATTESTATION_ID_MEID: + case Tag::ATTESTATION_ID_MANUFACTURER: + case Tag::ATTESTATION_ID_MODEL: + case Tag::ASSOCIATED_DATA: + case Tag::NONCE: + case Tag::AUTH_TOKEN: + return a.blob == b.blob; + + /* Enum tags */ + case Tag::PURPOSE: + return a.f.purpose == b.f.purpose; + case Tag::ALGORITHM: + return a.f.algorithm == b.f.algorithm; + case Tag::BLOCK_MODE: + return a.f.blockMode == b.f.blockMode; + case Tag::DIGEST: + return a.f.digest == b.f.digest; + case Tag::PADDING: + return a.f.paddingMode == b.f.paddingMode; + case Tag::EC_CURVE: + return a.f.ecCurve == b.f.ecCurve; + case Tag::BLOB_USAGE_REQUIREMENTS: + return a.f.keyBlobUsageRequirements == b.f.keyBlobUsageRequirements; + case Tag::USER_AUTH_TYPE: + return a.f.integer == b.f.integer; + case Tag::ORIGIN: + return a.f.origin == b.f.origin; + + /* Unsupported tags */ + case Tag::KDF: + return false; + } +} + +bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) { + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); +} + +bool operator==(const KeyCharacteristics& a, const KeyCharacteristics& b) { + // This isn't very efficient. Oh, well. + AuthorizationSet a_sw(a.softwareEnforced); + AuthorizationSet b_sw(b.softwareEnforced); + AuthorizationSet a_tee(b.teeEnforced); + AuthorizationSet b_tee(b.teeEnforced); + + a_sw.Sort(); + b_sw.Sort(); + a_tee.Sort(); + b_tee.Sort(); + + return a_sw == b_sw && a_tee == b_sw; +} + +::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) { + if (set.size() == 0) + os << "(Empty)" << ::std::endl; + else { + os << "\n"; + for (size_t i = 0; i < set.size(); ++i) + os << set[i] << ::std::endl; + } + return os; +} + +namespace test { +namespace { + +template +bool contains(hidl_vec& set, TypedTag ttag, ValueT expected_value) { + size_t count = std::count_if(set.begin(), set.end(), [&](const KeyParameter& param) { + return param.tag == tag && accessTagValue(ttag, param) == expected_value; + }); + return count == 1; +} + +template +bool contains(hidl_vec& set, TypedTag) { + size_t count = std::count_if(set.begin(), set.end(), + [&](const KeyParameter& param) { return param.tag == tag; }); + return count > 0; +} + +constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +string hex2str(string a) { + string b; + size_t num = a.size() / 2; + b.resize(num); + for (size_t i = 0; i < num; i++) { + b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); + } + return b; +} + +string rsa_key = hex2str("30820275020100300d06092a864886f70d01010105000482025f3082025b" + "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901" + "f234226cffe776521c5a77b9e389417b71c0b6a44d13afe4e4a2805d46c9" + "da2935adb1ff0c1f24ea06e62b20d776430a4d435157233c6f916783c30e" + "310fcbd89b85c2d56771169785ac12bca244abda72bfb19fc44d27c81e1d" + "92de284f4061edfd99280745ea6d2502030100010281801be0f04d9cae37" + "18691f035338308e91564b55899ffb5084d2460e6630257e05b3ceab0297" + "2dfabcd6ce5f6ee2589eb67911ed0fac16e43a444b8c861e544a05933657" + "72f8baf6b22fc9e3c5f1024b063ac080a7b2234cf8aee8f6c47bbf4fd3ac" + "e7240290bef16c0b3f7f3cdd64ce3ab5912cf6e32f39ab188358afcccd80" + "81024100e4b49ef50f765d3b24dde01aceaaf130f2c76670a91a61ae08af" + "497b4a82be6dee8fcdd5e3f7ba1cfb1f0c926b88f88c92bfab137fba2285" + "227b83c342ff7c55024100ddabb5839c4c7f6bf3d4183231f005b31aa58a" + "ffdda5c79e4cce217f6bc930dbe563d480706c24e9ebfcab28a6cdefd324" + "b77e1bf7251b709092c24ff501fd91024023d4340eda3445d8cd26c14411" + "da6fdca63c1ccd4b80a98ad52b78cc8ad8beb2842c1d280405bc2f6c1bea" + "214a1d742ab996b35b63a82a5e470fa88dbf823cdd02401b7b57449ad30d" + "1518249a5f56bb98294d4b6ac12ffc86940497a5a5837a6cf946262b4945" + "26d328c11e1126380fde04c24f916dec250892db09a6d77cdba351024077" + "62cd8f4d050da56bd591adb515d24d7ccd32cca0d05f866d583514bd7324" + "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3" + "3492d6"); + +string ec_key = hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30" + "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032" + "99986481f3a4d859a14403420004bf85d7720d07c25461683bc648b4778a" + "9a14dd8a024e3bdd8c7ddd9ab2b528bbc7aa1b51f14ebbbb0bd0ce21bcc4" + "1c6eb00083cf3376d11fd44949e0b2183bfe"); + +struct RSA_Delete { + void operator()(RSA* p) { RSA_free(p); } +}; + +X509* parse_cert_blob(const hidl_vec& blob) { + const uint8_t* p = blob.data(); + return d2i_X509(nullptr, &p, blob.size()); +} + +bool verify_chain(const hidl_vec>& chain) { + for (size_t i = 0; i < chain.size() - 1; ++i) { + auto& key_cert_blob = chain[i]; + auto& signing_cert_blob = chain[i + 1]; + + X509_Ptr key_cert(parse_cert_blob(key_cert_blob)); + X509_Ptr signing_cert(parse_cert_blob(signing_cert_blob)); + EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get()); + if (!key_cert.get() || !signing_cert.get()) return false; + + EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get())); + EXPECT_TRUE(!!signing_pubkey.get()); + if (!signing_pubkey.get()) return false; + + EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get())) + << "Verification of certificate " << i << " failed"; + } + + return true; +} + +// Extract attestation record from cert. Returned object is still part of cert; don't free it +// separately. +ASN1_OCTET_STRING* get_attestation_record(X509* certificate) { + ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */)); + EXPECT_TRUE(!!oid.get()); + if (!oid.get()) return nullptr; + + int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */); + EXPECT_NE(-1, location); + if (location == -1) return nullptr; + + X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location); + EXPECT_TRUE(!!attest_rec_ext); + if (!attest_rec_ext) return nullptr; + + ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext); + EXPECT_TRUE(!!attest_rec); + return attest_rec; +} + +bool tag_in_list(const KeyParameter& entry) { + // Attestations don't contain everything in key authorization lists, so we need to filter + // the key lists to produce the lists that we expect to match the attestations. + auto tag_list = { + Tag::USER_ID, Tag::INCLUDE_UNIQUE_ID, Tag::BLOB_USAGE_REQUIREMENTS, + Tag::EC_CURVE /* Tag::EC_CURVE will be included by KM2 implementations */, + }; + return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end(); +} + +AuthorizationSet filter_tags(const AuthorizationSet& set) { + AuthorizationSet filtered; + std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list); + return filtered; +} + +std::string make_string(const uint8_t* data, size_t length) { + return std::string(reinterpret_cast(data), length); +} + +template std::string make_string(const uint8_t (&a)[N]) { + return make_string(a, N); +} + +class HidlBuf : public hidl_vec { + typedef hidl_vec super; + + public: + HidlBuf() {} + HidlBuf(const super& other) : super(other) {} + HidlBuf(super&& other) : super(std::move(other)) {} + explicit HidlBuf(const std::string& other) : HidlBuf() { *this = other; } + + HidlBuf& operator=(const super& other) { + super::operator=(other); + return *this; + } + + HidlBuf& operator=(super&& other) { + super::operator=(std::move(other)); + return *this; + } + + HidlBuf& operator=(const string& other) { + resize(other.size()); + for (size_t i = 0; i < other.size(); ++i) { + (*this)[i] = static_cast(other[i]); + } + return *this; + } + + string to_string() const { return string(reinterpret_cast(data()), size()); } +}; + +constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF; + +} // namespace + +class KeymasterHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + void TearDown() override { + if (key_blob_.size()) { + EXPECT_EQ(ErrorCode::OK, DeleteKey()); + } + AbortIfNeeded(); + } + + // SetUpTestCase runs only once per test case, not once per test. + static void SetUpTestCase() { + keymaster_ = IKeymasterDevice::getService(service_name); + ASSERT_NE(keymaster_, nullptr); + + ASSERT_TRUE( + keymaster_ + ->getHardwareFeatures([&](bool isSecure, bool supportsEc, bool supportsSymmetric, + bool supportsAttestation, bool supportsAllDigests, + const hidl_string& name, const hidl_string& author) { + is_secure_ = isSecure; + supports_ec_ = supportsEc; + supports_symmetric_ = supportsSymmetric; + supports_attestation_ = supportsAttestation; + supports_all_digests_ = supportsAllDigests; + name_ = name; + author_ = author; + }) + .isOk()); + + os_version_ = ::keymaster::GetOsVersion(); + os_patch_level_ = ::keymaster::GetOsPatchlevel(); + } + + static void TearDownTestCase() { keymaster_.clear(); } + + static IKeymasterDevice& keymaster() { return *keymaster_; } + static uint32_t os_version() { return os_version_; } + static uint32_t os_patch_level() { return os_patch_level_; } + + AuthorizationSet UserAuths() { return AuthorizationSetBuilder().Authorization(TAG_USER_ID, 7); } + + ErrorCode GenerateKey(const AuthorizationSet& key_desc, HidlBuf* key_blob, + KeyCharacteristics* key_characteristics) { + EXPECT_NE(key_blob, nullptr); + EXPECT_NE(key_characteristics, nullptr); + EXPECT_EQ(0U, key_blob->size()); + + ErrorCode error; + EXPECT_TRUE(keymaster_ + ->generateKey(key_desc.hidl_data(), + [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob, + const KeyCharacteristics& hidl_key_characteristics) { + error = hidl_error; + *key_blob = hidl_key_blob; + *key_characteristics = hidl_key_characteristics; + }) + .isOk()); + // On error, blob & characteristics should be empty. + if (error != ErrorCode::OK) { + EXPECT_EQ(0U, key_blob->size()); + EXPECT_EQ(0U, (key_characteristics->softwareEnforced.size() + + key_characteristics->teeEnforced.size())); + } + return error; + } + + ErrorCode GenerateKey(const AuthorizationSet& key_desc) { + return GenerateKey(key_desc, &key_blob_, &key_characteristics_); + } + + ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format, + const string& key_material, HidlBuf* key_blob, + KeyCharacteristics* key_characteristics) { + ErrorCode error; + EXPECT_TRUE(keymaster_ + ->importKey(key_desc.hidl_data(), format, HidlBuf(key_material), + [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob, + const KeyCharacteristics& hidl_key_characteristics) { + error = hidl_error; + *key_blob = hidl_key_blob; + *key_characteristics = hidl_key_characteristics; + }) + .isOk()); + // On error, blob & characteristics should be empty. + if (error != ErrorCode::OK) { + EXPECT_EQ(0U, key_blob->size()); + EXPECT_EQ(0U, (key_characteristics->softwareEnforced.size() + + key_characteristics->teeEnforced.size())); + } + return error; + } + + ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format, + const string& key_material) { + return ImportKey(key_desc, format, key_material, &key_blob_, &key_characteristics_); + } + + ErrorCode ExportKey(KeyFormat format, const HidlBuf& key_blob, const HidlBuf& client_id, + const HidlBuf& app_data, HidlBuf* key_material) { + ErrorCode error; + EXPECT_TRUE( + keymaster_ + ->exportKey(format, key_blob, client_id, app_data, + [&](ErrorCode hidl_error_code, const HidlBuf& hidl_key_material) { + error = hidl_error_code; + *key_material = hidl_key_material; + }) + .isOk()); + // On error, blob should be empty. + if (error != ErrorCode::OK) { + EXPECT_EQ(0U, key_material->size()); + } + return error; + } + + ErrorCode ExportKey(KeyFormat format, HidlBuf* key_material) { + HidlBuf client_id, app_data; + return ExportKey(format, key_blob_, client_id, app_data, key_material); + } + + ErrorCode DeleteKey(HidlBuf* key_blob) { + ErrorCode error = keymaster_->deleteKey(*key_blob); + *key_blob = HidlBuf(); + return error; + } + + ErrorCode DeleteKey() { return DeleteKey(&key_blob_); } + + ErrorCode GetCharacteristics(const HidlBuf& key_blob, const HidlBuf& client_id, + const HidlBuf& app_data, KeyCharacteristics* key_characteristics) { + ErrorCode error; + keymaster_->getKeyCharacteristics( + key_blob, client_id, app_data, + [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_key_characteristics) { + error = hidl_error, *key_characteristics = hidl_key_characteristics; + }); + return error; + } + + ErrorCode GetCharacteristics(const HidlBuf& key_blob, KeyCharacteristics* key_characteristics) { + HidlBuf client_id, app_data; + return GetCharacteristics(key_blob, client_id, app_data, key_characteristics); + } + + ErrorCode Begin(KeyPurpose purpose, const HidlBuf& key_blob, const AuthorizationSet& in_params, + AuthorizationSet* out_params, OperationHandle* op_handle) { + SCOPED_TRACE("Begin"); + ErrorCode error; + OperationHandle saved_handle = *op_handle; + EXPECT_TRUE( + keymaster_ + ->begin(purpose, key_blob, in_params.hidl_data(), + [&](ErrorCode hidl_error, const hidl_vec& hidl_out_params, + uint64_t hidl_op_handle) { + error = hidl_error; + *out_params = hidl_out_params; + *op_handle = hidl_op_handle; + }) + .isOk()); + if (error != ErrorCode::OK) { + // Some implementations may modify *op_handle on error. + *op_handle = saved_handle; + } + return error; + } + + ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params, + AuthorizationSet* out_params) { + SCOPED_TRACE("Begin"); + EXPECT_EQ(kOpHandleSentinel, op_handle_); + return Begin(purpose, key_blob_, in_params, out_params, &op_handle_); + } + + ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params) { + SCOPED_TRACE("Begin"); + AuthorizationSet out_params; + ErrorCode error = Begin(purpose, in_params, &out_params); + EXPECT_TRUE(out_params.empty()); + return error; + } + + ErrorCode Update(OperationHandle op_handle, const AuthorizationSet& in_params, + const string& input, AuthorizationSet* out_params, string* output, + size_t* input_consumed) { + SCOPED_TRACE("Update"); + ErrorCode error; + EXPECT_TRUE(keymaster_ + ->update(op_handle, in_params.hidl_data(), HidlBuf(input), + [&](ErrorCode hidl_error, uint32_t hidl_input_consumed, + const hidl_vec& hidl_out_params, + const HidlBuf& hidl_output) { + error = hidl_error; + out_params->push_back(AuthorizationSet(hidl_out_params)); + output->append(hidl_output.to_string()); + *input_consumed = hidl_input_consumed; + }) + .isOk()); + return error; + } + + ErrorCode Update(const string& input, string* out, size_t* input_consumed) { + SCOPED_TRACE("Update"); + AuthorizationSet out_params; + ErrorCode error = Update(op_handle_, AuthorizationSet() /* in_params */, input, &out_params, + out, input_consumed); + EXPECT_TRUE(out_params.empty()); + return error; + } + + ErrorCode Finish(OperationHandle op_handle, const AuthorizationSet& in_params, + const string& input, const string& signature, AuthorizationSet* out_params, + string* output) { + SCOPED_TRACE("Finish"); + ErrorCode error; + EXPECT_TRUE( + keymaster_ + ->finish(op_handle, in_params.hidl_data(), HidlBuf(input), HidlBuf(signature), + [&](ErrorCode hidl_error, const hidl_vec& hidl_out_params, + const HidlBuf& hidl_output) { + error = hidl_error; + *out_params = hidl_out_params; + output->append(hidl_output.to_string()); + }) + .isOk()); + op_handle_ = kOpHandleSentinel; // So dtor doesn't Abort(). + return error; + } + + ErrorCode Finish(const string& message, string* output) { + SCOPED_TRACE("Finish"); + AuthorizationSet out_params; + string finish_output; + ErrorCode error = Finish(op_handle_, AuthorizationSet() /* in_params */, message, + "" /* signature */, &out_params, output); + if (error != ErrorCode::OK) { + return error; + } + EXPECT_EQ(0U, out_params.size()); + return error; + } + + ErrorCode Finish(const string& message, const string& signature, string* output) { + SCOPED_TRACE("Finish"); + AuthorizationSet out_params; + ErrorCode error = Finish(op_handle_, AuthorizationSet() /* in_params */, message, signature, + &out_params, output); + op_handle_ = kOpHandleSentinel; // So dtor doesn't Abort(). + if (error != ErrorCode::OK) { + return error; + } + EXPECT_EQ(0U, out_params.size()); + return error; + } + + ErrorCode Abort(OperationHandle op_handle) { + SCOPED_TRACE("Abort"); + auto retval = keymaster_->abort(op_handle); + EXPECT_TRUE(retval.isOk()); + return retval; + } + + void AbortIfNeeded() { + SCOPED_TRACE("AbortIfNeeded"); + if (op_handle_ != kOpHandleSentinel) { + EXPECT_EQ(ErrorCode::OK, Abort(op_handle_)); + op_handle_ = kOpHandleSentinel; + } + } + + ErrorCode AttestKey(const HidlBuf& key_blob, const AuthorizationSet& attest_params, + hidl_vec>* cert_chain) { + SCOPED_TRACE("AttestKey"); + ErrorCode error; + keymaster_->attestKey( + key_blob, attest_params.hidl_data(), + [&](ErrorCode hidl_error, const hidl_vec>& hidl_cert_chain) { + error = hidl_error; + *cert_chain = hidl_cert_chain; + }); + return error; + } + + ErrorCode AttestKey(const AuthorizationSet& attest_params, + hidl_vec>* cert_chain) { + SCOPED_TRACE("AttestKey"); + return AttestKey(key_blob_, attest_params, cert_chain); + } + + string ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const string& message, + const AuthorizationSet& in_params, AuthorizationSet* out_params) { + SCOPED_TRACE("ProcessMessage"); + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, + Begin(operation, key_blob, in_params, &begin_out_params, &op_handle_)); + + string unused; + AuthorizationSet finish_params; + AuthorizationSet finish_out_params; + string output; + EXPECT_EQ(ErrorCode::OK, + Finish(op_handle_, finish_params, message, unused, &finish_out_params, &output)); + op_handle_ = kOpHandleSentinel; + + out_params->push_back(begin_out_params); + out_params->push_back(finish_out_params); + return output; + } + + string SignMessage(const HidlBuf& key_blob, const string& message, + const AuthorizationSet& params) { + SCOPED_TRACE("SignMessage"); + AuthorizationSet out_params; + string signature = ProcessMessage(key_blob, KeyPurpose::SIGN, message, params, &out_params); + EXPECT_TRUE(out_params.empty()); + return signature; + } + + string SignMessage(const string& message, const AuthorizationSet& params) { + SCOPED_TRACE("SignMessage"); + return SignMessage(key_blob_, message, params); + } + + string MacMessage(const string& message, Digest digest, size_t mac_length) { + SCOPED_TRACE("MacMessage"); + return SignMessage( + key_blob_, message, + AuthorizationSetBuilder().Digest(digest).Authorization(TAG_MAC_LENGTH, mac_length)); + } + + void CheckHmacTestVector(const string& key, const string& message, Digest digest, + const string& expected_mac) { + SCOPED_TRACE("CheckHmacTestVector"); + ASSERT_EQ(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(key.size() * 8) + .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8) + .Digest(digest), + KeyFormat::RAW, key)); + string signature = MacMessage(message, digest, expected_mac.size() * 8); + EXPECT_EQ(expected_mac, signature) << "Test vector didn't match for digest " << (int)digest; + DeleteKey(); + } + + void CheckAesCtrTestVector(const string& key, const string& nonce, const string& message, + const string& expected_ciphertext) { + SCOPED_TRACE("CheckAesCtrTestVector"); + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(key.size() * 8) + .BlockMode(BlockMode::CTR) + .Authorization(TAG_CALLER_NONCE) + .Padding(PaddingMode::NONE), + KeyFormat::RAW, key)); + + auto params = AuthorizationSetBuilder() + .Authorization(TAG_NONCE, nonce.data(), nonce.size()) + .BlockMode(BlockMode::CTR) + .Padding(PaddingMode::NONE); + AuthorizationSet out_params; + string ciphertext = EncryptMessage(key_blob_, message, params, &out_params); + EXPECT_EQ(expected_ciphertext, ciphertext); + } + + void VerifyMessage(const HidlBuf& key_blob, const string& message, const string& signature, + const AuthorizationSet& params) { + SCOPED_TRACE("VerifyMessage"); + AuthorizationSet begin_out_params; + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params, &op_handle_)); + + string unused; + AuthorizationSet finish_params; + AuthorizationSet finish_out_params; + string output; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, signature, + &finish_out_params, &output)); + op_handle_ = kOpHandleSentinel; + EXPECT_TRUE(output.empty()); + } + + void VerifyMessage(const string& message, const string& signature, + const AuthorizationSet& params) { + SCOPED_TRACE("VerifyMessage"); + VerifyMessage(key_blob_, message, signature, params); + } + + string EncryptMessage(const HidlBuf& key_blob, const string& message, + const AuthorizationSet& in_params, AuthorizationSet* out_params) { + SCOPED_TRACE("EncryptMessage"); + return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params); + } + + string EncryptMessage(const string& message, const AuthorizationSet& params, + AuthorizationSet* out_params) { + SCOPED_TRACE("EncryptMessage"); + return EncryptMessage(key_blob_, message, params, out_params); + } + + string EncryptMessage(const string& message, const AuthorizationSet& params) { + SCOPED_TRACE("EncryptMessage"); + AuthorizationSet out_params; + string ciphertext = EncryptMessage(message, params, &out_params); + EXPECT_TRUE(out_params.empty()) + << "Output params should be empty. Contained: " << out_params; + return ciphertext; + } + + string DecryptMessage(const HidlBuf& key_blob, const string& ciphertext, + const AuthorizationSet& params) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet out_params; + string plaintext = + ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params); + EXPECT_TRUE(out_params.empty()); + return plaintext; + } + + string DecryptMessage(const string& ciphertext, const AuthorizationSet& params) { + SCOPED_TRACE("DecryptMessage"); + return DecryptMessage(key_blob_, ciphertext, params); + } + + template + void CheckKm0CryptoParam(TypedTag ttag, ValueT expected) { + SCOPED_TRACE("CheckKm0CryptoParam"); + if (is_secure_) { + EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag)); + } else { + EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag)); + } + } + + template + void CheckKm1CryptoParam(TypedTag ttag, ValueT expected) { + SCOPED_TRACE("CheckKm1CryptoParam"); + if (is_secure_ && supports_symmetric_) { + EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag)); + } else { + EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag)); + } + } + + template + void CheckKm2CryptoParam(TypedTag ttag, ValueT expected) { + SCOPED_TRACE("CheckKm2CryptoParam"); + if (supports_attestation_) { + EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag)); + } else if (!supports_symmetric_ /* KM version < 1 or SW */) { + EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected)); + EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag)); + } + } + + void CheckOrigin() { + SCOPED_TRACE("CheckOrigin"); + if (is_secure_ && supports_symmetric_) { + EXPECT_TRUE( + contains(key_characteristics_.teeEnforced, TAG_ORIGIN, KeyOrigin::IMPORTED)); + } else if (is_secure_) { + EXPECT_TRUE(contains(key_characteristics_.teeEnforced, TAG_ORIGIN, KeyOrigin::UNKNOWN)); + } else { + EXPECT_TRUE( + contains(key_characteristics_.softwareEnforced, TAG_ORIGIN, KeyOrigin::IMPORTED)); + } + } + + static bool IsSecure() { return is_secure_; } + static bool SupportsEc() { return supports_ec_; } + static bool SupportsSymmetric() { return supports_symmetric_; } + static bool SupportsAllDigests() { return supports_all_digests_; } + static bool SupportsAttestation() { return supports_attestation_; } + + static bool Km2Profile() { + return SupportsAttestation() && SupportsAllDigests() && SupportsSymmetric() && + SupportsEc() && IsSecure(); + } + + static bool Km1Profile() { + return !SupportsAttestation() && SupportsSymmetric() && SupportsEc() && IsSecure(); + } + + static bool Km0Profile() { + return !SupportsAttestation() && !SupportsAllDigests() && !SupportsSymmetric() && + IsSecure(); + } + + static bool SwOnlyProfile() { + return !SupportsAttestation() && !SupportsAllDigests() && !SupportsSymmetric() && + !SupportsEc() && !IsSecure(); + } + + HidlBuf key_blob_; + KeyCharacteristics key_characteristics_; + OperationHandle op_handle_ = kOpHandleSentinel; + + private: + static sp keymaster_; + static uint32_t os_version_; + static uint32_t os_patch_level_; + + static bool is_secure_; + static bool supports_ec_; + static bool supports_symmetric_; + static bool supports_attestation_; + static bool supports_all_digests_; + static hidl_string name_; + static hidl_string author_; +}; + +uint32_t expected_keymaster_version() { + if (!KeymasterHidlTest::IsSecure()) return 2; // SW is KM2 + + uint32_t keymaster_version = 0; + if (KeymasterHidlTest::SupportsSymmetric()) keymaster_version = 1; + if (KeymasterHidlTest::SupportsAttestation()) keymaster_version = 2; + return keymaster_version; +} + +bool verify_attestation_record(const string& challenge, AuthorizationSet expected_sw_enforced, + AuthorizationSet expected_tee_enforced, + const hidl_vec& attestation_cert) { + + X509_Ptr cert(parse_cert_blob(attestation_cert)); + EXPECT_TRUE(!!cert.get()); + if (!cert.get()) return false; + + ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get()); + EXPECT_TRUE(!!attest_rec); + if (!attest_rec) return false; + + AuthorizationSet att_sw_enforced; + AuthorizationSet att_tee_enforced; + uint32_t att_attestation_version; + uint32_t att_keymaster_version; + SecurityLevel att_attestation_security_level; + SecurityLevel att_keymaster_security_level; + HidlBuf att_challenge; + HidlBuf att_unique_id; + EXPECT_EQ(ErrorCode::OK, + parse_attestation_record(attest_rec->data, // + attest_rec->length, // + &att_attestation_version, // + &att_attestation_security_level, // + &att_keymaster_version, // + &att_keymaster_security_level, // + &att_challenge, // + &att_sw_enforced, // + &att_tee_enforced, // + &att_unique_id)); + + EXPECT_EQ(1U, att_attestation_version); + EXPECT_EQ(expected_keymaster_version(), att_keymaster_version); + EXPECT_EQ(KeymasterHidlTest::IsSecure() ? SecurityLevel::TRUSTED_ENVIRONMENT + : SecurityLevel::SOFTWARE, + att_keymaster_security_level); + EXPECT_EQ(KeymasterHidlTest::SupportsAttestation() ? SecurityLevel::TRUSTED_ENVIRONMENT + : SecurityLevel::SOFTWARE, + att_attestation_security_level); + + EXPECT_EQ(challenge.length(), att_challenge.size()); + EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length())); + + att_sw_enforced.Sort(); + expected_sw_enforced.Sort(); + EXPECT_EQ(filter_tags(expected_sw_enforced), filter_tags(att_sw_enforced)); + + att_tee_enforced.Sort(); + expected_tee_enforced.Sort(); + EXPECT_EQ(filter_tags(expected_tee_enforced), filter_tags(att_tee_enforced)); + + return true; +} + +sp KeymasterHidlTest::keymaster_; +uint32_t KeymasterHidlTest::os_version_; +uint32_t KeymasterHidlTest::os_patch_level_; +bool KeymasterHidlTest::is_secure_; +bool KeymasterHidlTest::supports_ec_; +bool KeymasterHidlTest::supports_symmetric_; +bool KeymasterHidlTest::supports_all_digests_; +bool KeymasterHidlTest::supports_attestation_; +hidl_string KeymasterHidlTest::name_; +hidl_string KeymasterHidlTest::author_; + +typedef KeymasterHidlTest KeymasterVersionTest; + +/* + * KeymasterVersionTest.SensibleFeatures: + * + * Queries keymaster to find the set of features it supports. Fails if the combination doesn't + * correspond to any well-defined keymaster version. + */ +TEST_F(KeymasterVersionTest, SensibleFeatures) { + EXPECT_TRUE(Km2Profile() || Km1Profile() || Km0Profile() || SwOnlyProfile()) + << "Keymaster feature set doesn't fit any reasonable profile. Reported features:" + << "SupportsAttestation [" << SupportsAttestation() << "], " + << "SupportsSymmetric [" << SupportsSymmetric() << "], " + << "SupportsAllDigests [" << SupportsAllDigests() << "], " + << "SupportsEc [" << SupportsEc() << "], " + << "IsSecure [" << IsSecure() << "]"; +} + +class NewKeyGenerationTest : public KeymasterHidlTest { + protected: + void CheckBaseParams(const KeyCharacteristics& keyCharacteristics) { + // TODO(swillden): Distinguish which params should be in which auth list. + + AuthorizationSet auths(keyCharacteristics.teeEnforced); + auths.push_back(AuthorizationSet(keyCharacteristics.softwareEnforced)); + + EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED)); + + EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN)); + EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY)); + EXPECT_TRUE(auths.Contains(TAG_USER_ID, 7)) + << "User ID should be 7, was " << auths.GetTagValue(TAG_USER_ID); + + // Verify that App ID, App data and ROT are NOT included. + EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST)); + EXPECT_FALSE(auths.Contains(TAG_APPLICATION_ID)); + EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA)); + + // Check that some unexpected tags/values are NOT present. + EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT)); + EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT)); + EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301)); + + // Now check that unspecified, defaulted tags are correct. + EXPECT_TRUE(auths.Contains(TAG_CREATION_DATETIME)); + + if (SupportsAttestation()) { + EXPECT_TRUE(auths.Contains(TAG_OS_VERSION, os_version())) + << "OS version is " << os_version() << " key reported " + << auths.GetTagValue(TAG_OS_VERSION); + EXPECT_TRUE(auths.Contains(TAG_OS_PATCHLEVEL, os_patch_level())) + << "OS patch level is " << os_patch_level() << " key reported " + << auths.GetTagValue(TAG_OS_PATCHLEVEL); + } + } +}; + +/* + * NewKeyGenerationTest.Rsa + * + * Verifies that keymaster can generate all required RSA key sizes, and that the resulting keys have + * correct characteristics. + */ +TEST_F(NewKeyGenerationTest, Rsa) { + for (auto key_size : {1024, 2048, 3072, 4096}) { + HidlBuf key_blob; + KeyCharacteristics key_characteristics; + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorizations(UserAuths()), + &key_blob, &key_characteristics)); + + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + + AuthorizationSet crypto_params; + if (IsSecure()) { + crypto_params = key_characteristics.teeEnforced; + } else { + crypto_params = key_characteristics.softwareEnforced; + } + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)); + EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 3)); + + EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob)); + } +} + +/* + * NewKeyGenerationTest.RsaNoDefaultSize + * + * Verifies that failing to specify a key size for RSA key generation returns UNSUPPORTED_KEY_SIZE. + */ +TEST_F(NewKeyGenerationTest, RsaNoDefaultSize) { + ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, Algorithm::RSA) + .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3) + .SigningKey())); +} + +/* + * NewKeyGenerationTest.Ecdsa + * + * Verifies that keymaster can generate all required EC key sizes, and that the resulting keys have + * correct characteristics. + */ +TEST_F(NewKeyGenerationTest, Ecdsa) { + for (auto key_size : {224, 256, 384, 521}) { + HidlBuf key_blob; + KeyCharacteristics key_characteristics; + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(key_size) + .Digest(Digest::NONE) + .Authorizations(UserAuths()), + &key_blob, &key_characteristics)); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + + AuthorizationSet crypto_params; + if (IsSecure()) { + crypto_params = key_characteristics.teeEnforced; + } else { + crypto_params = key_characteristics.softwareEnforced; + } + + EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC)); + EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size)); + + EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob)); + } +} + +/* + * NewKeyGenerationTest.EcdsaDefaultSize + * + * Verifies that failing to specify a key size for EC key generation returns UNSUPPORTED_KEY_SIZE. + */ +TEST_F(NewKeyGenerationTest, EcdsaDefaultSize) { + ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, Algorithm::EC) + .SigningKey() + .Digest(Digest::NONE))); +} + +/* + * NewKeyGenerationTest.EcdsaInvalidSize + * + * Verifies that failing to specify an invalid key size for EC key generation returns + * UNSUPPORTED_KEY_SIZE. + */ +TEST_F(NewKeyGenerationTest, EcdsaInvalidSize) { + ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(Digest::NONE))); +} + +/* + * NewKeyGenerationTest.EcdsaMismatchKeySize + * + * Verifies that specifying mismatched key size and curve for EC key generation returns + * INVALID_ARGUMENT. + */ +TEST_F(NewKeyGenerationTest, EcdsaMismatchKeySize) { + ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, + GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(224) + .Authorization(TAG_EC_CURVE, EcCurve::P_256) + .Digest(Digest::NONE))) + << "(Possibly b/36233343)"; +} + +TEST_F(NewKeyGenerationTest, EcdsaAllValidSizes) { + size_t valid_sizes[] = {224, 256, 384, 521}; + for (size_t size : valid_sizes) { + EXPECT_EQ(ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(Digest::NONE))) + << "Failed to generate size: " << size; + DeleteKey(); + } +} + +/* + * NewKeyGenerationTest.EcdsaAllValidCurves + * + * Verifies that keymaster supports all required EC curves. + */ +TEST_F(NewKeyGenerationTest, EcdsaAllValidCurves) { + EcCurve curves[] = {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}; + for (auto curve : curves) { + EXPECT_EQ( + ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(curve).Digest(Digest::SHA_2_512))) + << "Failed to generate key on curve: " << curve; + DeleteKey(); + } +} + +/* + * NewKeyGenerationTest.Hmac + * + * Verifies that keymaster supports all required digests, and that the resulting keys have correct + * characteristics. + */ +TEST_F(NewKeyGenerationTest, Hmac) { + for (auto digest : {Digest::MD5, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, + Digest::SHA_2_384, Digest::SHA_2_512}) { + HidlBuf key_blob; + KeyCharacteristics key_characteristics; + constexpr size_t key_size = 128; + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(key_size) + .Digest(digest) + .Authorization(TAG_MIN_MAC_LENGTH, 128) + .Authorizations(UserAuths()), + &key_blob, &key_characteristics)); + + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + + AuthorizationSet teeEnforced = key_characteristics.teeEnforced; + AuthorizationSet softwareEnforced = key_characteristics.softwareEnforced; + if (SupportsAttestation() || SupportsAllDigests()) { + // Either KM2, which must support all, or KM1 that claims full support + EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC)); + EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size)); + } else if (SupportsSymmetric()) { + if (digest == Digest::SHA1 || digest == Digest::SHA_2_256) { + // KM1 must support SHA1 and SHA256 in hardware + EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC)); + EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size)); + } else { + // Othere digests may or may not be supported + EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC) || + softwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC)); + EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size) || + softwareEnforced.Contains(TAG_KEY_SIZE, key_size)); + } + } else { + // KM0 and SW KM do all digests in SW. + EXPECT_TRUE(softwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC)); + EXPECT_TRUE(softwareEnforced.Contains(TAG_KEY_SIZE, key_size)); + } + + EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob)); + } +} + +/* + * NewKeyGenerationTest.HmacCheckKeySizes + * + * Verifies that keymaster supports all key sizes, and rejects all invalid key sizes. + */ +TEST_F(NewKeyGenerationTest, HmacCheckKeySizes) { + for (size_t key_size = 0; key_size <= 512; ++key_size) { + if (key_size < 64 || key_size % 8 != 0) { + // To keep this test from being very slow, we only test a random fraction of non-byte + // key sizes. We test only ~10% of such cases. Since there are 392 of them, we expect + // to run ~40 of them in each run. + if (key_size % 8 == 0 || random() % 10 == 0) { + EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(key_size) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))) + << "HMAC key size " << key_size << " invalid (Possibly b/33462346)"; + } + } else { + EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(key_size) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))); + DeleteKey(); + } + } +} + +/* + * NewKeyGenerationTest.HmacCheckMinMacLengths + * + * Verifies that keymaster supports all required MAC lengths and rejects all invalid lengths. This + * test is probabilistic in order to keep the runtime down, but any failure prints out the specific + * MAC length that failed, so reproducing a failed run will be easy. + */ +TEST_F(NewKeyGenerationTest, HmacCheckMinMacLengths) { + for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) { + if (min_mac_length < 64 || min_mac_length % 8 != 0) { + // To keep this test from being very long, we only test a random fraction of non-byte + // lengths. We test only ~10% of such cases. Since there are 172 of them, we expect to + // run ~17 of them in each run. + if (min_mac_length % 8 == 0 || random() % 10 == 0) { + EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length))) + << "HMAC min mac length " << min_mac_length << " invalid."; + } + } else { + EXPECT_EQ(ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length))); + DeleteKey(); + } + } +} + +/* + * NewKeyGenerationTest.HmacMultipleDigests + * + * Verifies that keymaster rejects HMAC key generation with multiple specified digest algorithms. + */ +TEST_F(NewKeyGenerationTest, HmacMultipleDigests) { + ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(Digest::SHA1) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); +} + +/* + * NewKeyGenerationTest.HmacDigestNone + * + * Verifies that keymaster rejects HMAC key generation with no digest or Digest::NONE + */ +TEST_F(NewKeyGenerationTest, HmacDigestNone) { + ASSERT_EQ( + ErrorCode::UNSUPPORTED_DIGEST, + GenerateKey(AuthorizationSetBuilder().HmacKey(128).Authorization(TAG_MIN_MAC_LENGTH, 128))); + + ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(Digest::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); +} + +typedef KeymasterHidlTest GetKeyCharacteristicsTest; + +/* + * GetKeyCharacteristicsTest.HmacDigestNone + * + * Verifies that getKeyCharacteristics functions, and that generated and retrieved key + * characteristics match. + */ +TEST_F(GetKeyCharacteristicsTest, SimpleRsa) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + + KeyCharacteristics retrieved_chars; + ASSERT_EQ(ErrorCode::OK, GetCharacteristics(key_blob_, &retrieved_chars)); + + AuthorizationSet gen_sw = key_characteristics_.softwareEnforced; + AuthorizationSet gen_tee = key_characteristics_.teeEnforced; + AuthorizationSet retrieved_sw = retrieved_chars.softwareEnforced; + AuthorizationSet retrieved_tee = retrieved_chars.teeEnforced; + + EXPECT_EQ(gen_sw, retrieved_sw); + EXPECT_EQ(gen_tee, retrieved_tee); +} + +typedef KeymasterHidlTest SigningOperationsTest; + +/* + * SigningOperationsTest.RsaSuccess + * + * Verifies that raw RSA signature operations succeed. + */ +TEST_F(SigningOperationsTest, RsaSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED))); + string message = "12345678901234567890123456789012"; + string signature = SignMessage( + message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); +} + +/* + * SigningOperationsTest.RsaPssSha256Success + * + * Verifies that RSA-PSS signature operations succeed. + */ +TEST_F(SigningOperationsTest, RsaPssSha256Success) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::RSA_PSS) + .Authorization(TAG_NO_AUTH_REQUIRED))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature = SignMessage( + message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS)); +} + +/* + * SigningOperationsTest.RsaPaddingNoneDoesNotAllowOther + * + * Verifies that keymaster rejects signature operations that specify a padding mode when the key + * supports only unpadded operations. + */ +TEST_F(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::NONE))); + string message = "12345678901234567890123456789012"; + string signature; + + EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder() + .Digest(Digest::NONE) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); +} + +/* + * SigningOperationsTest.RsaPkcs1Sha256Success + * + * Verifies that digested RSA-PKCS1 signature operations succeed. + */ +TEST_F(SigningOperationsTest, RsaPkcs1Sha256Success) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + string message(1024, 'a'); + string signature = SignMessage(message, AuthorizationSetBuilder() + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)); +} + +/* + * SigningOperationsTest.RsaPkcs1NoDigestSuccess + * + * Verifies that undigested RSA-PKCS1 signature operations succeed. + */ +TEST_F(SigningOperationsTest, RsaPkcs1NoDigestSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + string message(53, 'a'); + string signature = SignMessage( + message, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)); +} + +/* + * SigningOperationsTest.RsaPkcs1NoDigestTooLarge + * + * Verifies that undigested RSA-PKCS1 signature operations fail with the correct error code when + * given a too-long message. + */ +TEST_F(SigningOperationsTest, RsaPkcs1NoDigestTooLong) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + string message(129, 'a'); + + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder() + .Digest(Digest::NONE) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + string signature; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &signature)); +} + +/* + * SigningOperationsTest.RsaPssSha512TooSmallKey + * + * Verifies that undigested RSA-PSS signature operations fail with the correct error code when + * used with a key that is too small for the message. + * + * A PSS-padded message is of length salt_size + digest_size + 16 (sizes in bits), and the keymaster + * specification requires that salt_size == digest_size, so the message will be digest_size * 2 + + * 16. Such a message can only be signed by a given key if the key is at least that size. This test + * uses SHA512, which has a digest_size == 512, so the message size is 1040 bits, too large for a + * 1024-bit key. + */ +TEST_F(SigningOperationsTest, RsaPssSha512TooSmallKey) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::SHA_2_512) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::RSA_PSS))); + EXPECT_EQ( + ErrorCode::INCOMPATIBLE_DIGEST, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::SHA_2_512).Padding(PaddingMode::RSA_PSS))) + << "(Possibly b/33346750)"; +} + +/* + * SigningOperationsTest.RsaNoPaddingTooLong + * + * Verifies that raw RSA signature operations fail with the correct error code when + * given a too-long message. + */ +TEST_F(SigningOperationsTest, RsaNoPaddingTooLong) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + // One byte too long + string message(1024 / 8 + 1, 'a'); + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder() + .Digest(Digest::NONE) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + string result; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result)); + + // Very large message that should exceed the transfer buffer size of any reasonable TEE. + message = string(128 * 1024, 'a'); + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder() + .Digest(Digest::NONE) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result)); +} + +/* + * SigningOperationsTest.RsaAbort + * + * Verifies that operations can be aborted correctly. Uses an RSA signing operation for the test, + * but the behavior should be algorithm and purpose-independent. + */ +TEST_F(SigningOperationsTest, RsaAbort) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Padding(PaddingMode::NONE))); + + ASSERT_EQ(ErrorCode::OK, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); + EXPECT_EQ(ErrorCode::OK, Abort(op_handle_)); + + // Another abort should fail + EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort(op_handle_)); + + // Set to sentinel, so TearDown() doesn't try to abort again. + op_handle_ = kOpHandleSentinel; +} + +/* + * SigningOperationsTest.RsaUnsupportedPadding + * + * Verifies that RSA operations fail with the correct error (but key gen succeeds) when used with a + * padding mode inappropriate for RSA. + */ +TEST_F(SigningOperationsTest, RsaUnsupportedPadding) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Digest(Digest::SHA_2_256 /* supported digest */) + .Padding(PaddingMode::PKCS7))); + ASSERT_EQ( + ErrorCode::UNSUPPORTED_PADDING_MODE, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7))); +} + +/* + * SigningOperationsTest.RsaPssNoDigest + * + * Verifies that RSA PSS operations fail when no digest is used. PSS requires a digest. + */ +TEST_F(SigningOperationsTest, RsaNoDigest) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Digest(Digest::NONE) + .Padding(PaddingMode::RSA_PSS))); + ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS))); + + ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS))); +} + +/* + * SigningOperationsTest.RsaPssNoDigest + * + * Verifies that RSA operations fail when no padding mode is specified. PaddingMode::NONE is + * supported in some cases (as validated in other tests), but a mode must be specified. + */ +TEST_F(SigningOperationsTest, RsaNoPadding) { + // Padding must be specified + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaKey(1024, 3) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SigningKey() + .Digest(Digest::NONE))); + ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE, + Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE))); +} + +/* + * SigningOperationsTest.RsaShortMessage + * + * Verifies that raw RSA signatures succeed with a message shorter than the key size. + */ +TEST_F(SigningOperationsTest, RsaTooShortMessage) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + + // Barely shorter + string message(1024 / 8 - 1, 'a'); + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); + + // Much shorter + message = "a"; + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); +} + +/* + * SigningOperationsTest.RsaSignWithEncryptionKey + * + * Verifies that RSA encryption keys cannot be used to sign. + */ +TEST_F(SigningOperationsTest, RsaSignWithEncryptionKey) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, + Begin(KeyPurpose::SIGN, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE))); +} + +/* + * SigningOperationsTest.RsaSignTooLargeMessage + * + * Verifies that attempting a raw signature of a message which is the same length as the key, but + * numerically larger than the public modulus, fails with the correct error. + */ +TEST_F(SigningOperationsTest, RsaSignTooLargeMessage) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + + // Largest possible message will always be larger than the public modulus. + string message(1024 / 8, static_cast(0xff)); + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + string signature; + ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &signature)); +} + +/* + * SigningOperationsTest.EcdsaAllSizesAndHashes + * + * Verifies that ECDSA operations succeed with all possible key sizes and hashes. + */ +TEST_F(SigningOperationsTest, EcdsaAllSizesAndHashes) { + for (auto key_size : {224, 256, 384, 521}) { + for (auto digest : { + Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384, + Digest::SHA_2_512, + }) { + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(key_size) + .Digest(digest)); + EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with size " << key_size + << " and digest " << digest; + if (error != ErrorCode::OK) continue; + + string message(1024, 'a'); + if (digest == Digest::NONE) message.resize(key_size / 8); + SignMessage(message, AuthorizationSetBuilder().Digest(digest)); + DeleteKey(); + } + } +} + +/* + * SigningOperationsTest.EcdsaAllCurves + * + * Verifies that ECDSA operations succeed with all possible curves. + */ +TEST_F(SigningOperationsTest, EcdsaAllCurves) { + for (auto curve : {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}) { + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::SHA_2_256)); + EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve; + if (error != ErrorCode::OK) continue; + + string message(1024, 'a'); + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); + DeleteKey(); + } +} + +/* + * SigningOperationsTest.EcdsaNoDigestHugeData + * + * Verifies that ECDSA operations support very large messages, even without digesting. This should + * work because ECDSA actually only signs the leftmost L_n bits of the message, however large it may + * be. Not using digesting is a bad idea, but in some cases digesting is done by the framework. + */ +TEST_F(SigningOperationsTest, EcdsaNoDigestHugeData) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(224) + .Digest(Digest::NONE))); + string message(64 * 1024, 'a'); + SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE)); +} + +/* + * SigningOperationsTest.AesEcbSign + * + * Verifies that attempts to use AES keys to sign fail in the correct way. + */ +TEST_F(SigningOperationsTest, AesEcbSign) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .SigningKey() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB))) + << "(Possibly b/36252957)"; + + AuthorizationSet out_params; + EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, + Begin(KeyPurpose::SIGN, AuthorizationSet() /* in_params */, &out_params)) + << "(Possibly b/36233187)"; + + EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, + Begin(KeyPurpose::VERIFY, AuthorizationSet() /* in_params */, &out_params)) + << "(Possibly b/36233187)"; +} + +/* + * SigningOperationsTest.HmacAllDigests + * + * Verifies that HMAC works with all digests. + */ +TEST_F(SigningOperationsTest, HmacAllDigests) { + for (auto digest : {Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384, + Digest::SHA_2_512}) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(128) + .Digest(digest) + .Authorization(TAG_MIN_MAC_LENGTH, 160))) + << "Failed to create HMAC key with digest " << digest; + string message = "12345678901234567890123456789012"; + string signature = MacMessage(message, digest, 160); + EXPECT_EQ(160U / 8U, signature.size()) + << "Failed to sign with HMAC key with digest " << digest; + DeleteKey(); + } +} + +/* + * SigningOperationsTest.HmacSha256TooLargeMacLength + * + * Verifies that HMAC fails in the correct way when asked to generate a MAC larger than the digest + * size. + */ +TEST_F(SigningOperationsTest, HmacSha256TooLargeMacLength) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(128) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))); + AuthorizationSet output_params; + EXPECT_EQ( + ErrorCode::UNSUPPORTED_MAC_LENGTH, + Begin( + KeyPurpose::SIGN, key_blob_, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 264), + &output_params, &op_handle_)); +} + +/* + * SigningOperationsTest.HmacSha256TooSmallMacLength + * + * Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the + * specified minimum MAC length. + */ +TEST_F(SigningOperationsTest, HmacSha256TooSmallMacLength) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(128) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + AuthorizationSet output_params; + EXPECT_EQ( + ErrorCode::INVALID_MAC_LENGTH, + Begin( + KeyPurpose::SIGN, key_blob_, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 120), + &output_params, &op_handle_)); +} + +/* + * SigningOperationsTest.HmacRfc4231TestCase3 + * + * Validates against the test vectors from RFC 4231 test case 3. + */ +TEST_F(SigningOperationsTest, HmacRfc4231TestCase3) { + string key(20, 0xaa); + string message(50, 0xdd); + uint8_t sha_224_expected[] = { + 0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a, + 0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea, + }; + uint8_t sha_256_expected[] = { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, + 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, + 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe, + }; + uint8_t sha_384_expected[] = { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0, + 0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d, + 0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27, + }; + uint8_t sha_512_expected[] = { + 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c, + 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8, + 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, + 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37, + 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb, + }; + + CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected)); +} + +/* + * SigningOperationsTest.HmacRfc4231TestCase5 + * + * Validates against the test vectors from RFC 4231 test case 5. + */ +TEST_F(SigningOperationsTest, HmacRfc4231TestCase5) { + string key(20, 0x0c); + string message = "Test With Truncation"; + + uint8_t sha_224_expected[] = { + 0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37, + 0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8, + }; + uint8_t sha_256_expected[] = { + 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0, + 0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b, + }; + uint8_t sha_384_expected[] = { + 0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23, + 0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97, + }; + uint8_t sha_512_expected[] = { + 0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53, + 0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6, + }; + + CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected)); +} + +/* + * SigningOperationsTest.HmacRfc4231TestCase6 + * + * Validates against the test vectors from RFC 4231 test case 6. + */ +TEST_F(SigningOperationsTest, HmacRfc4231TestCase6) { + string key(131, 0xaa); + string message = "Test Using Larger Than Block-Size Key - Hash Key First"; + + uint8_t sha_224_expected[] = { + 0x95, 0xe9, 0xa0, 0xdb, 0x96, 0x20, 0x95, 0xad, 0xae, 0xbe, 0x9b, 0x2d, 0x6f, 0x0d, + 0xbc, 0xe2, 0xd4, 0x99, 0xf1, 0x12, 0xf2, 0xd2, 0xb7, 0x27, 0x3f, 0xa6, 0x87, 0x0e, + }; + uint8_t sha_256_expected[] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, + 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, + 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54, + }; + uint8_t sha_384_expected[] = { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 0x88, 0xd2, 0xc6, 0x3a, + 0x04, 0x1b, 0xc5, 0xb4, 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 0x0c, 0x2e, 0xf6, 0xab, + 0x40, 0x30, 0xfe, 0x82, 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52, + }; + uint8_t sha_512_expected[] = { + 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1, 0xdd, + 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 0x12, 0x1b, + 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25, + 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 0x95, 0xe6, 0x4f, 0x73, + 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98, + }; + + CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected)); +} + +/* + * SigningOperationsTest.HmacRfc4231TestCase7 + * + * Validates against the test vectors from RFC 4231 test case 7. + */ +TEST_F(SigningOperationsTest, HmacRfc4231TestCase7) { + string key(131, 0xaa); + string message = "This is a test using a larger than block-size key and a larger than " + "block-size data. The key needs to be hashed before being used by the HMAC " + "algorithm."; + + uint8_t sha_224_expected[] = { + 0x3a, 0x85, 0x41, 0x66, 0xac, 0x5d, 0x9f, 0x02, 0x3f, 0x54, 0xd5, 0x17, 0xd0, 0xb3, + 0x9d, 0xbd, 0x94, 0x67, 0x70, 0xdb, 0x9c, 0x2b, 0x95, 0xc9, 0xf6, 0xf5, 0x65, 0xd1, + }; + uint8_t sha_256_expected[] = { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, + 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, + 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2, + }; + uint8_t sha_384_expected[] = { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25, + 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31, + 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e, + }; + uint8_t sha_512_expected[] = { + 0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, + 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5, + 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, + 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb, + 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58, + }; + + CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected)); +} + +typedef KeymasterHidlTest VerificationOperationsTest; + +/* + * VerificationOperationsTest.RsaSuccess + * + * Verifies that a simple RSA signature/verification sequence succeeds. + */ +TEST_F(VerificationOperationsTest, RsaSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + string message = "12345678901234567890123456789012"; + string signature = SignMessage( + message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); + VerifyMessage(message, signature, + AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)); +} + +/* + * VerificationOperationsTest.RsaSuccess + * + * Verifies RSA signature/verification for all padding modes and digests. + */ +TEST_F(VerificationOperationsTest, RsaAllPaddingsAndDigests) { + Digest digest = Digest::SHA_2_256; + ASSERT_EQ(ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(2048, 3) + .Digest(Digest::NONE, Digest::MD5, Digest::SHA1, Digest::SHA_2_224, + Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512) + .Padding(PaddingMode::NONE) + .Padding(PaddingMode::RSA_PSS) + .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN))); + + string message(128, 'a'); + string corrupt_message(message); + ++corrupt_message[corrupt_message.size() / 2]; + + for (auto padding : + {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) { + + for (auto digest : {Digest::NONE, Digest::MD5, Digest::SHA1, Digest::SHA_2_224, + Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512}) { + if (padding == PaddingMode::NONE && digest != Digest::NONE) { + // Digesting only makes sense with padding. + continue; + } + + if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) { + // PSS requires digesting. + continue; + } + + string signature = + SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding)); + VerifyMessage(message, signature, + AuthorizationSetBuilder().Digest(digest).Padding(padding)); + + if (digest != Digest::NONE) { + // Verify with OpenSSL. + HidlBuf pubkey; + ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey)); + + const uint8_t* p = pubkey.data(); + EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size())); + ASSERT_TRUE(pkey.get()); + + EVP_MD_CTX digest_ctx; + EVP_MD_CTX_init(&digest_ctx); + EVP_PKEY_CTX* pkey_ctx; + const EVP_MD* md = openssl_digest(digest); + ASSERT_NE(md, nullptr); + EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr /* engine */, + pkey.get())); + + switch (padding) { + case PaddingMode::RSA_PSS: + EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING), 0); + EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, EVP_MD_size(md)), 0); + break; + case PaddingMode::RSA_PKCS1_1_5_SIGN: + // PKCS1 is the default; don't need to set anything. + break; + default: + FAIL(); + break; + } + + EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size())); + EXPECT_EQ(1, EVP_DigestVerifyFinal( + &digest_ctx, reinterpret_cast(signature.data()), + signature.size())); + EVP_MD_CTX_cleanup(&digest_ctx); + } + + // Corrupt signature shouldn't verify. + string corrupt_signature(signature); + ++corrupt_signature[corrupt_signature.size() / 2]; + + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, + AuthorizationSetBuilder().Digest(digest).Padding(padding))); + string result; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result)); + + // Corrupt message shouldn't verify + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, + AuthorizationSetBuilder().Digest(digest).Padding(padding))); + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result)); + } + } +} + +/* + * VerificationOperationsTest.RsaSuccess + * + * Verifies ECDSA signature/verification for all digests and curves. + */ +TEST_F(VerificationOperationsTest, EcdsaAllDigestsAndCurves) { + auto digests = { + Digest::NONE, Digest::SHA1, Digest::SHA_2_224, + Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512, + }; + + string message = "1234567890"; + string corrupt_message = "2234567890"; + for (auto curve : {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}) { + ErrorCode error = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(digests)); + EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve; + if (error != ErrorCode::OK) { + continue; + } + + for (auto digest : digests) { + string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest)); + VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest)); + + // Verify with OpenSSL + if (digest != Digest::NONE) { + HidlBuf pubkey; + ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey)) + << curve << ' ' << digest; + + const uint8_t* p = pubkey.data(); + EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size())); + ASSERT_TRUE(pkey.get()); + + EVP_MD_CTX digest_ctx; + EVP_MD_CTX_init(&digest_ctx); + EVP_PKEY_CTX* pkey_ctx; + const EVP_MD* md = openssl_digest(digest); + + EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr /* engine */, + pkey.get())) + << curve << ' ' << digest; + + EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size())) + << curve << ' ' << digest; + + EXPECT_EQ(1, EVP_DigestVerifyFinal( + &digest_ctx, reinterpret_cast(signature.data()), + signature.size())) + << curve << ' ' << digest; + + EVP_MD_CTX_cleanup(&digest_ctx); + } + + // Corrupt signature shouldn't verify. + string corrupt_signature(signature); + ++corrupt_signature[corrupt_signature.size() / 2]; + + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest))) + << curve << ' ' << digest; + + string result; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result)) + << curve << ' ' << digest; + + // Corrupt message shouldn't verify + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest))) + << curve << ' ' << digest; + + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result)) + << curve << ' ' << digest; + } + + ASSERT_EQ(ErrorCode::OK, DeleteKey()); + } +} + +/* + * VerificationOperationsTest.HmacSigningKeyCannotVerify + * + * Verifies HMAC signing and verification, but that a signing key cannot be used to verify. + */ +TEST_F(VerificationOperationsTest, HmacSigningKeyCannotVerify) { + string key_material = "HelloThisIsAKey"; + + HidlBuf signing_key, verification_key; + KeyCharacteristics signing_key_chars, verification_key_chars; + EXPECT_EQ(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_ALGORITHM, Algorithm::HMAC) + .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) + .Digest(Digest::SHA1) + .Authorization(TAG_MIN_MAC_LENGTH, 160), + KeyFormat::RAW, key_material, &signing_key, &signing_key_chars)); + EXPECT_EQ(ErrorCode::OK, + ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_ALGORITHM, Algorithm::HMAC) + .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY) + .Digest(Digest::SHA1) + .Authorization(TAG_MIN_MAC_LENGTH, 160), + KeyFormat::RAW, key_material, &verification_key, &verification_key_chars)); + + string message = "This is a message."; + string signature = SignMessage( + signing_key, message, + AuthorizationSetBuilder().Digest(Digest::SHA1).Authorization(TAG_MAC_LENGTH, 160)); + + // Signing key should not work. + AuthorizationSet out_params; + EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, + Begin(KeyPurpose::VERIFY, signing_key, AuthorizationSetBuilder().Digest(Digest::SHA1), + &out_params, &op_handle_)); + + // Verification key should work. + VerifyMessage(verification_key, message, signature, + AuthorizationSetBuilder().Digest(Digest::SHA1)); + + EXPECT_EQ(ErrorCode::OK, DeleteKey(&signing_key)); + EXPECT_EQ(ErrorCode::OK, DeleteKey(&verification_key)); +} + +typedef KeymasterHidlTest ExportKeyTest; + +/* + * ExportKeyTest.RsaUnsupportedKeyFormat + * + * Verifies that attempting to export RSA keys in PKCS#8 format fails with the correct error. + */ +TEST_F(ExportKeyTest, RsaUnsupportedKeyFormat) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + HidlBuf export_data; + ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::PKCS8, &export_data)); +} + +/* + * ExportKeyTest.RsaCorruptedKeyBlob + * + * Verifies that attempting to export RSA keys from corrupted key blobs fails. This is essentially + * a poor-man's key blob fuzzer. + */ +// Disabled due to b/33385206 +TEST_F(ExportKeyTest, DISABLED_RsaCorruptedKeyBlob) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(256, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE))); + for (size_t i = 0; i < key_blob_.size(); ++i) { + HidlBuf corrupted(key_blob_); + ++corrupted[i]; + + HidlBuf export_data; + EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, + ExportKey(KeyFormat::X509, corrupted, HidlBuf(), HidlBuf(), &export_data)) + << "Blob corrupted at offset " << i << " erroneously accepted as valid"; + } +} + +/* + * ExportKeyTest.RsaCorruptedKeyBlob + * + * Verifies that attempting to export ECDSA keys from corrupted key blobs fails. This is + * essentially a poor-man's key blob fuzzer. + */ +// Disabled due to b/33385206 +TEST_F(ExportKeyTest, DISABLED_EcCorruptedKeyBlob) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE))); + for (size_t i = 0; i < key_blob_.size(); ++i) { + HidlBuf corrupted(key_blob_); + ++corrupted[i]; + + HidlBuf export_data; + EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, + ExportKey(KeyFormat::X509, corrupted, HidlBuf(), HidlBuf(), &export_data)) + << "Blob corrupted at offset " << i << " erroneously accepted as valid"; + } +} + +/* + * ExportKeyTest.AesKeyUnexportable + * + * Verifies that attempting to export AES keys fails in the expected way. + */ +TEST_F(ExportKeyTest, AesKeyUnexportable) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .EcbMode() + .Padding(PaddingMode::NONE))); + + HidlBuf export_data; + EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::X509, &export_data)); + EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::PKCS8, &export_data)); + EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::RAW, &export_data)); +} +typedef KeymasterHidlTest ImportKeyTest; + +/* + * ImportKeyTest.RsaSuccess + * + * Verifies that importing and using an RSA key pair works correctly. + */ +TEST_F(ImportKeyTest, RsaSuccess) { + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 65537) + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::RSA_PSS), + KeyFormat::PKCS8, rsa_key)); + + CheckKm0CryptoParam(TAG_ALGORITHM, Algorithm::RSA); + CheckKm0CryptoParam(TAG_KEY_SIZE, 1024U); + CheckKm0CryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U); + CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256); + CheckKm1CryptoParam(TAG_PADDING, PaddingMode::RSA_PSS); + CheckOrigin(); + + string message(1024 / 8, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS); + string signature = SignMessage(message, params); + VerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.RsaKeySizeMismatch + * + * Verifies that importing an RSA key pair with a size that doesn't match the key fails in the + * correct way. + */ +TEST_F(ImportKeyTest, RsaKeySizeMismatch) { + ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .RsaSigningKey(2048 /* Doesn't match key */, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE), + KeyFormat::PKCS8, rsa_key)); +} + +/* + * ImportKeyTest.RsaPublicExponentMismatch + * + * Verifies that importing an RSA key pair with a public exponent that doesn't match the key fails + * in the correct way. + */ +TEST_F(ImportKeyTest, RsaPublicExponentMismatch) { + ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 3 /* Doesn't match key */) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE), + KeyFormat::PKCS8, rsa_key)); +} + +/* + * ImportKeyTest.EcdsaSuccess + * + * Verifies that importing and using an ECDSA key pair works correctly. + */ +TEST_F(ImportKeyTest, EcdsaSuccess) { + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(256) + .Digest(Digest::SHA_2_256), + KeyFormat::PKCS8, ec_key)) + << "(Possibly b/33945114)"; + + CheckKm0CryptoParam(TAG_ALGORITHM, Algorithm::EC); + CheckKm0CryptoParam(TAG_KEY_SIZE, 256U); + CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256); + CheckKm2CryptoParam(TAG_EC_CURVE, EcCurve::P_256); + + CheckOrigin(); + + string message(32, 'a'); + auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256); + string signature = SignMessage(message, params); + VerifyMessage(message, signature, params); +} + +/* + * ImportKeyTest.EcdsaSizeMismatch + * + * Verifies that importing an ECDSA key pair with a size that doesn't match the key fails in the + * correct way. + */ +TEST_F(ImportKeyTest, EcdsaSizeMismatch) { + ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(224 /* Doesn't match key */) + .Digest(Digest::NONE), + KeyFormat::PKCS8, ec_key)); +} + +/* + * ImportKeyTest.EcdsaCurveMismatch + * + * Verifies that importing an ECDSA key pair with a curve that doesn't match the key fails in the + * correct way. + */ +TEST_F(ImportKeyTest, EcdsaCurveMismatch) { + if (SupportsSymmetric() && !SupportsAttestation()) { + // KM1 hardware doesn't know about curves + return; + } + + ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */) + .Digest(Digest::NONE), + KeyFormat::PKCS8, ec_key)) + << "(Possibly b/36233241)"; +} + +/* + * ImportKeyTest.AesSuccess + * + * Verifies that importing and using an AES key works. + */ +TEST_F(ImportKeyTest, AesSuccess) { + string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(key.size() * 8) + .EcbMode() + .Padding(PaddingMode::PKCS7), + KeyFormat::RAW, key)); + + CheckKm1CryptoParam(TAG_ALGORITHM, Algorithm::AES); + CheckKm1CryptoParam(TAG_KEY_SIZE, 128U); + CheckKm1CryptoParam(TAG_PADDING, PaddingMode::PKCS7); + CheckKm1CryptoParam(TAG_BLOCK_MODE, BlockMode::ECB); + CheckOrigin(); + + string message = "Hello World!"; + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + string ciphertext = EncryptMessage(message, params); + string plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(message, plaintext); +} + +/* + * ImportKeyTest.AesSuccess + * + * Verifies that importing and using an HMAC key works. + */ +TEST_F(ImportKeyTest, HmacKeySuccess) { + string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(key.size() * 8) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256), + KeyFormat::RAW, key)); + + CheckKm1CryptoParam(TAG_ALGORITHM, Algorithm::HMAC); + CheckKm1CryptoParam(TAG_KEY_SIZE, 128U); + CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256); + CheckOrigin(); + + string message = "Hello World!"; + string signature = MacMessage(message, Digest::SHA_2_256, 256); + VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); +} + +typedef KeymasterHidlTest EncryptionOperationsTest; + +/* + * EncryptionOperationsTest.RsaNoPaddingSuccess + * + * Verifies that raw RSA encryption works. + */ +TEST_F(EncryptionOperationsTest, RsaNoPaddingSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::NONE))); + + string message = string(1024 / 8, 'a'); + auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); + string ciphertext1 = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext2.size()); + + // Unpadded RSA is deterministic + EXPECT_EQ(ciphertext1, ciphertext2); +} + +/* + * EncryptionOperationsTest.RsaNoPaddingShortMessage + * + * Verifies that raw RSA encryption of short messages works. + */ +TEST_F(EncryptionOperationsTest, RsaNoPaddingShortMessage) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::NONE))); + + string message = "1"; + auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); + + string ciphertext = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext.size()); + + string expected_plaintext = string(1024 / 8 - 1, 0) + message; + string plaintext = DecryptMessage(ciphertext, params); + + EXPECT_EQ(expected_plaintext, plaintext); + + // Degenerate case, encrypting a numeric 1 yields 0x00..01 as the ciphertext. + message = static_cast(1); + ciphertext = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext.size()); + EXPECT_EQ(ciphertext, string(1024 / 8 - 1, 0) + message); +} + +/* + * EncryptionOperationsTest.RsaNoPaddingTooLong + * + * Verifies that raw RSA encryption of too-long messages fails in the expected way. + */ +TEST_F(EncryptionOperationsTest, RsaNoPaddingTooLong) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::NONE))); + + string message(1024 / 8 + 1, 'a'); + + auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params)); + + string result; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result)); +} + +/* + * EncryptionOperationsTest.RsaNoPaddingTooLong + * + * Verifies that raw RSA encryption of too-large (numerically) messages fails in the expected way. + */ +TEST_F(EncryptionOperationsTest, RsaNoPaddingTooLarge) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::NONE))); + + HidlBuf exported; + ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &exported)); + + const uint8_t* p = exported.data(); + EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, exported.size())); + RSA_Ptr rsa(EVP_PKEY_get1_RSA(pkey.get())); + + size_t modulus_len = BN_num_bytes(rsa->n); + ASSERT_EQ(1024U / 8, modulus_len); + std::unique_ptr modulus_buf(new uint8_t[modulus_len]); + BN_bn2bin(rsa->n, modulus_buf.get()); + + // The modulus is too big to encrypt. + string message(reinterpret_cast(modulus_buf.get()), modulus_len); + + auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params)); + + string result; + EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &result)); + + // One smaller than the modulus is okay. + BN_sub(rsa->n, rsa->n, BN_value_one()); + modulus_len = BN_num_bytes(rsa->n); + ASSERT_EQ(1024U / 8, modulus_len); + BN_bn2bin(rsa->n, modulus_buf.get()); + message = string(reinterpret_cast(modulus_buf.get()), modulus_len); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params)); + EXPECT_EQ(ErrorCode::OK, Finish(message, &result)); +} + +/* + * EncryptionOperationsTest.RsaOaepSuccess + * + * Verifies that RSA-OAEP encryption operations work, with all digests. + */ +TEST_F(EncryptionOperationsTest, RsaOaepSuccess) { + auto digests = {Digest::MD5, Digest::SHA1, Digest::SHA_2_224, + Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512}; + + size_t key_size = 2048; // Need largish key for SHA-512 test. + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(key_size, 3) + .Padding(PaddingMode::RSA_OAEP) + .Digest(digests))); + + string message = "Hello"; + + for (auto digest : digests) { + auto params = AuthorizationSetBuilder().Digest(digest).Padding(PaddingMode::RSA_OAEP); + string ciphertext1 = EncryptMessage(message, params); + if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl; + EXPECT_EQ(key_size / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(message, params); + EXPECT_EQ(key_size / 8, ciphertext2.size()); + + // OAEP randomizes padding so every result should be different (with astronomically high + // probability). + EXPECT_NE(ciphertext1, ciphertext2); + + string plaintext1 = DecryptMessage(ciphertext1, params); + EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest; + string plaintext2 = DecryptMessage(ciphertext2, params); + EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest; + + // Decrypting corrupted ciphertext should fail. + size_t offset_to_corrupt = random() % ciphertext1.size(); + char corrupt_byte; + do { + corrupt_byte = static_cast(random() % 256); + } while (corrupt_byte == ciphertext1[offset_to_corrupt]); + ciphertext1[offset_to_corrupt] = corrupt_byte; + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string result; + EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result)); + EXPECT_EQ(0U, result.size()); + } +} + +/* + * EncryptionOperationsTest.RsaOaepInvalidDigest + * + * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate + * without a digest. + */ +TEST_F(EncryptionOperationsTest, RsaOaepInvalidDigest) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::RSA_OAEP) + .Digest(Digest::NONE))); + string message = "Hello World!"; + + auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE); + EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::ENCRYPT, params)); +} + +/* + * EncryptionOperationsTest.RsaOaepInvalidDigest + * + * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt with a + * different digest than was used to encrypt. + */ +TEST_F(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::RSA_OAEP) + .Digest(Digest::SHA_2_256, Digest::SHA_2_224))); + string message = "Hello World!"; + string ciphertext = EncryptMessage( + message, + AuthorizationSetBuilder().Digest(Digest::SHA_2_224).Padding(PaddingMode::RSA_OAEP)); + + EXPECT_EQ( + ErrorCode::OK, + Begin(KeyPurpose::DECRYPT, + AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_OAEP))); + string result; + EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result)); + EXPECT_EQ(0U, result.size()); +} + +/* + * EncryptionOperationsTest.RsaOaepTooLarge + * + * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to encrypt a + * too-large message. + */ +TEST_F(EncryptionOperationsTest, RsaOaepTooLarge) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::RSA_OAEP) + .Digest(Digest::SHA1))); + constexpr size_t digest_size = 160 /* SHA1 */ / 8; + constexpr size_t oaep_overhead = 2 * digest_size + 2; + string message(1024 / 8 - oaep_overhead + 1, 'a'); + EXPECT_EQ(ErrorCode::OK, + Begin(KeyPurpose::ENCRYPT, + AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::SHA1))); + string result; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result)); + EXPECT_EQ(0U, result.size()); +} + +/* + * EncryptionOperationsTest.RsaPkcs1Success + * + * Verifies that RSA PKCS encryption/decrypts works. + */ +TEST_F(EncryptionOperationsTest, RsaPkcs1Success) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT))); + + string message = "Hello World!"; + auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT); + string ciphertext1 = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(message, params); + EXPECT_EQ(1024U / 8, ciphertext2.size()); + + // PKCS1 v1.5 randomizes padding so every result should be different. + EXPECT_NE(ciphertext1, ciphertext2); + + string plaintext = DecryptMessage(ciphertext1, params); + EXPECT_EQ(message, plaintext); + + // Decrypting corrupted ciphertext should fail. + size_t offset_to_corrupt = random() % ciphertext1.size(); + char corrupt_byte; + do { + corrupt_byte = static_cast(random() % 256); + } while (corrupt_byte == ciphertext1[offset_to_corrupt]); + ciphertext1[offset_to_corrupt] = corrupt_byte; + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string result; + EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result)); + EXPECT_EQ(0U, result.size()); +} + +/* + * EncryptionOperationsTest.RsaPkcs1TooLarge + * + * Verifies that RSA PKCS encryption fails in the correct way when the mssage is too large. + */ +TEST_F(EncryptionOperationsTest, RsaPkcs1TooLarge) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaEncryptionKey(1024, 3) + .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT))); + string message(1024 / 8 - 10, 'a'); + + auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params)); + string result; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result)); + EXPECT_EQ(0U, result.size()); +} + +/* + * EncryptionOperationsTest.EcdsaEncrypt + * + * Verifies that attempting to use ECDSA keys to encrypt fails in the correct way. + */ +TEST_F(EncryptionOperationsTest, EcdsaEncrypt) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(224) + .Digest(Digest::NONE))); + auto params = AuthorizationSetBuilder().Digest(Digest::NONE); + ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params)) + << "(Possibly b/33543625)"; + ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params)) + << "(Possibly b/33543625)"; +} + +/* + * EncryptionOperationsTest.HmacEncrypt + * + * Verifies that attempting to use HMAC keys to encrypt fails in the correct way. + */ +TEST_F(EncryptionOperationsTest, HmacEncrypt) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(128) + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + auto params = AuthorizationSetBuilder() + .Digest(Digest::SHA_2_256) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params)) + << "(Possibly b/33543625)"; + ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params)) + << "(Possibly b/33543625)"; +} + +/* + * EncryptionOperationsTest.AesEcbRoundTripSuccess + * + * Verifies that AES ECB mode works. + */ +TEST_F(EncryptionOperationsTest, AesEcbRoundTripSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::NONE))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE); + + // Two-block message. + string message = "12345678901234567890123456789012"; + string ciphertext1 = EncryptMessage(message, params); + EXPECT_EQ(message.size(), ciphertext1.size()); + + string ciphertext2 = EncryptMessage(string(message), params); + EXPECT_EQ(message.size(), ciphertext2.size()); + + // ECB is deterministic. + EXPECT_EQ(ciphertext1, ciphertext2); + + string plaintext = DecryptMessage(ciphertext1, params); + EXPECT_EQ(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesEcbRoundTripSuccess + * + * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified. + */ +TEST_F(EncryptionOperationsTest, AesWrongMode) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CBC) + .Padding(PaddingMode::NONE))); + // Two-block message. + string message = "12345678901234567890123456789012"; + EXPECT_EQ( + ErrorCode::INCOMPATIBLE_BLOCK_MODE, + Begin(KeyPurpose::ENCRYPT, + AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE))); +} + +/* + * EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize + * + * Verifies that AES encryption fails in the correct way when provided an input that is not a + * multiple of the block size and no padding is specified. + */ +TEST_F(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::NONE))); + // Message is slightly shorter than two blocks. + string message(16 * 2 - 1, 'a'); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params)); + string ciphertext; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext)); + EXPECT_EQ(0U, ciphertext.size()); +} + +/* + * EncryptionOperationsTest.AesEcbPkcs7Padding + * + * Verifies that AES PKCS7 padding works for any message length. + */ +TEST_F(EncryptionOperationsTest, AesEcbPkcs7Padding) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::PKCS7))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + + // Try various message lengths; all should work. + for (size_t i = 0; i < 32; ++i) { + string message(i, 'a'); + string ciphertext = EncryptMessage(message, params); + EXPECT_EQ(i + 16 - (i % 16), ciphertext.size()); + string plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(message, plaintext); + } +} + +/* + * EncryptionOperationsTest.AesEcbWrongPadding + * + * Verifies that AES enryption fails in the correct way when an unauthorized padding mode is + * specified. + */ +TEST_F(EncryptionOperationsTest, AesEcbWrongPadding) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::NONE))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + + // Try various message lengths; all should fail + for (size_t i = 0; i < 32; ++i) { + string message(i, 'a'); + EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params)); + } +} + +/* + * EncryptionOperationsTest.AesEcbPkcs7PaddingCorrupted + * + * Verifies that AES decryption fails in the correct way when the padding is corrupted. + */ +TEST_F(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::ECB) + .Padding(PaddingMode::PKCS7))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7); + + string message = "a"; + string ciphertext = EncryptMessage(message, params); + EXPECT_EQ(16U, ciphertext.size()); + EXPECT_NE(ciphertext, message); + ++ciphertext[ciphertext.size() / 2]; + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string plaintext; + EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &plaintext)); +} + +HidlBuf CopyIv(const AuthorizationSet& set) { + auto iv = set.GetTagValue(TAG_NONCE); + EXPECT_TRUE(iv.isOk()); + return iv.value(); +} + +/* + * EncryptionOperationsTest.AesCtrRoundTripSuccess + * + * Verifies that AES CTR mode works. + */ +TEST_F(EncryptionOperationsTest, AesCtrRoundTripSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CTR) + .Padding(PaddingMode::NONE))); + + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE); + + string message = "123"; + AuthorizationSet out_params; + string ciphertext1 = EncryptMessage(message, params, &out_params); + HidlBuf iv1 = CopyIv(out_params); + EXPECT_EQ(16U, iv1.size()); + + EXPECT_EQ(message.size(), ciphertext1.size()); + + out_params.Clear(); + string ciphertext2 = EncryptMessage(message, params, &out_params); + HidlBuf iv2 = CopyIv(out_params); + EXPECT_EQ(16U, iv2.size()); + + // IVs should be random, so ciphertexts should differ. + EXPECT_NE(ciphertext1, ciphertext2); + + auto params_iv1 = + AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv1); + auto params_iv2 = + AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv2); + + string plaintext = DecryptMessage(ciphertext1, params_iv1); + EXPECT_EQ(message, plaintext); + plaintext = DecryptMessage(ciphertext2, params_iv2); + EXPECT_EQ(message, plaintext); + + // Using the wrong IV will result in a "valid" decryption, but the data will be garbage. + plaintext = DecryptMessage(ciphertext1, params_iv2); + EXPECT_NE(message, plaintext); + plaintext = DecryptMessage(ciphertext2, params_iv1); + EXPECT_NE(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesIncremental + * + * Verifies that AES works, all modes, when provided data in various size increments. + */ +TEST_F(EncryptionOperationsTest, AesIncremental) { + auto block_modes = { + BlockMode::ECB, BlockMode::CBC, BlockMode::CTR, BlockMode::GCM, + }; + + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(block_modes) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + for (int increment = 1; increment <= 240; ++increment) { + for (auto block_mode : block_modes) { + string message(240, 'a'); + auto params = AuthorizationSetBuilder() + .BlockMode(block_mode) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128) /* for GCM */; + + AuthorizationSet output_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &output_params)); + + string ciphertext; + size_t input_consumed; + string to_send; + for (size_t i = 0; i < message.size(); i += increment) { + to_send.append(message.substr(i, increment)); + EXPECT_EQ(ErrorCode::OK, Update(to_send, &ciphertext, &input_consumed)); + to_send = to_send.substr(input_consumed); + + switch (block_mode) { + case BlockMode::ECB: + case BlockMode::CBC: + // Implementations must take as many blocks as possible, leaving less than + // a block. + EXPECT_LE(to_send.length(), 16U); + break; + case BlockMode::GCM: + case BlockMode::CTR: + // Implementations must always take all the data. + EXPECT_EQ(0U, to_send.length()); + break; + } + } + EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext)) << "Error sending " << to_send; + + switch (block_mode) { + case BlockMode::GCM: + EXPECT_EQ(message.size() + 16, ciphertext.size()); + break; + case BlockMode::CTR: + EXPECT_EQ(message.size(), ciphertext.size()); + break; + case BlockMode::CBC: + case BlockMode::ECB: + EXPECT_EQ(message.size() + message.size() % 16, ciphertext.size()); + break; + } + + auto iv = output_params.GetTagValue(TAG_NONCE); + switch (block_mode) { + case BlockMode::CBC: + case BlockMode::GCM: + case BlockMode::CTR: + ASSERT_TRUE(iv.isOk()) << "No IV for block mode " << block_mode; + EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv.value().size()); + params.push_back(TAG_NONCE, iv.value()); + break; + + case BlockMode::ECB: + EXPECT_FALSE(iv.isOk()) << "ECB mode should not generate IV"; + break; + } + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)) + << "Decrypt begin() failed for block mode " << block_mode; + + string plaintext; + for (size_t i = 0; i < ciphertext.size(); i += increment) { + to_send.append(ciphertext.substr(i, increment)); + EXPECT_EQ(ErrorCode::OK, Update(to_send, &plaintext, &input_consumed)); + to_send = to_send.substr(input_consumed); + } + ErrorCode error = Finish(to_send, &plaintext); + ASSERT_EQ(ErrorCode::OK, error) + << "Decryption failed for block mode " << block_mode << " and increment " + << increment << " (Possibly b/33584622)"; + if (error == ErrorCode::OK) { + ASSERT_EQ(message, plaintext) << "Decryption didn't match for block mode " + << block_mode << " and increment " << increment; + } + } + } +} + +struct AesCtrSp80038aTestVector { + const char* key; + const char* nonce; + const char* plaintext; + const char* ciphertext; +}; + +// These test vectors are taken from +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5. +static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = { + // AES-128 + { + "2b7e151628aed2a6abf7158809cf4f3c", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff" + "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee", + }, + // AES-192 + { + "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94" + "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050", + }, + // AES-256 + { + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5" + "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6", + }, +}; + +/* + * EncryptionOperationsTest.AesCtrSp80038aTestVector + * + * Verifies AES CTR implementation against SP800-38A test vectors. + */ +TEST_F(EncryptionOperationsTest, AesCtrSp80038aTestVector) { + for (size_t i = 0; i < 3; i++) { + const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]); + const string key = hex2str(test.key); + const string nonce = hex2str(test.nonce); + const string plaintext = hex2str(test.plaintext); + const string ciphertext = hex2str(test.ciphertext); + CheckAesCtrTestVector(key, nonce, plaintext, ciphertext); + } +} + +/* + * EncryptionOperationsTest.AesCtrIncompatiblePaddingMode + * + * Verifies that keymaster rejects use of CTR mode with PKCS7 padding in the correct way. + */ +TEST_F(EncryptionOperationsTest, AesCtrIncompatiblePaddingMode) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CTR) + .Padding(PaddingMode::PKCS7))); + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE); + EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params)); +} + +/* + * EncryptionOperationsTest.AesCtrInvalidCallerNonce + * + * Verifies that keymaster fails correctly when the user supplies an incorrect-size nonce. + */ +TEST_F(EncryptionOperationsTest, AesCtrInvalidCallerNonce) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CTR) + .Authorization(TAG_CALLER_NONCE) + .Padding(PaddingMode::NONE))); + + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CTR) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf(string(1, 'a'))); + EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params)); + + params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CTR) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf(string(15, 'a'))); + EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params)); + + params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CTR) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf(string(17, 'a'))); + EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params)); +} + +/* + * EncryptionOperationsTest.AesCtrInvalidCallerNonce + * + * Verifies that keymaster fails correctly when the user supplies an incorrect-size nonce. + */ +TEST_F(EncryptionOperationsTest, AesCbcRoundTripSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CBC) + .Padding(PaddingMode::NONE))); + // Two-block message. + string message = "12345678901234567890123456789012"; + auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE); + AuthorizationSet out_params; + string ciphertext1 = EncryptMessage(message, params, &out_params); + HidlBuf iv1 = CopyIv(out_params); + EXPECT_EQ(message.size(), ciphertext1.size()); + + out_params.Clear(); + + string ciphertext2 = EncryptMessage(message, params, &out_params); + HidlBuf iv2 = CopyIv(out_params); + EXPECT_EQ(message.size(), ciphertext2.size()); + + // IVs should be random, so ciphertexts should differ. + EXPECT_NE(ciphertext1, ciphertext2); + + params.push_back(TAG_NONCE, iv1); + string plaintext = DecryptMessage(ciphertext1, params); + EXPECT_EQ(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesCallerNonce + * + * Verifies that AES caller-provided nonces work correctly. + */ +TEST_F(EncryptionOperationsTest, AesCallerNonce) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CBC) + .Authorization(TAG_CALLER_NONCE) + .Padding(PaddingMode::NONE))); + + string message = "12345678901234567890123456789012"; + + // Don't specify nonce, should get a random one. + AuthorizationSetBuilder params = + AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE); + AuthorizationSet out_params; + string ciphertext = EncryptMessage(message, params, &out_params); + EXPECT_EQ(message.size(), ciphertext.size()); + EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size()); + + params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value()); + string plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(message, plaintext); + + // Now specify a nonce, should also work. + params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CBC) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf("abcdefghijklmnop")); + out_params.Clear(); + ciphertext = EncryptMessage(message, params, &out_params); + + // Decrypt with correct nonce. + plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(message, plaintext); + + // Try with wrong nonce. + params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CBC) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf("aaaaaaaaaaaaaaaa")); + plaintext = DecryptMessage(ciphertext, params); + EXPECT_NE(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesCallerNonceProhibited + * + * Verifies that caller-provided nonces are not permitted when not specified in the key + * authorizations. + */ +TEST_F(EncryptionOperationsTest, AesCallerNonceProhibited) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::CBC) + .Padding(PaddingMode::NONE))); + + string message = "12345678901234567890123456789012"; + + // Don't specify nonce, should get a random one. + AuthorizationSetBuilder params = + AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE); + AuthorizationSet out_params; + string ciphertext = EncryptMessage(message, params, &out_params); + EXPECT_EQ(message.size(), ciphertext.size()); + EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size()); + + params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value()); + string plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(message, plaintext); + + // Now specify a nonce, should fail + params = AuthorizationSetBuilder() + .BlockMode(BlockMode::CBC) + .Padding(PaddingMode::NONE) + .Authorization(TAG_NONCE, HidlBuf("abcdefghijklmnop")); + out_params.Clear(); + EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED, Begin(KeyPurpose::ENCRYPT, params, &out_params)); +} + +/* + * EncryptionOperationsTest.AesGcmRoundTripSuccess + * + * Verifies that AES GCM mode works. + */ +TEST_F(EncryptionOperationsTest, AesGcmRoundTripSuccess) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + + auto begin_params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto update_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params)) + << "Begin encrypt"; + string ciphertext; + AuthorizationSet update_out_params; + ASSERT_EQ(ErrorCode::OK, + Finish(op_handle_, update_params, message, "", &update_out_params, &ciphertext)); + + // Grab nonce + begin_params.push_back(begin_out_params); + + // Decrypt. + ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt"; + string plaintext; + size_t input_consumed; + ASSERT_EQ(ErrorCode::OK, Update(op_handle_, update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext)); + + EXPECT_EQ(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesGcmTooShortTag + * + * Verifies that AES GCM mode fails correctly when a too-short tag length is specified. + */ +TEST_F(EncryptionOperationsTest, AesGcmTooShortTag) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string message = "123456789012345678901234567890123456"; + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 96); + + EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::ENCRYPT, params)); +} + +/* + * EncryptionOperationsTest.AesGcmTooShortTagOnDecrypt + * + * Verifies that AES GCM mode fails correctly when a too-short tag is provided to decryption. + */ +TEST_F(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto finish_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params)); + EXPECT_EQ(1U, begin_out_params.size()); + ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE).isOk()); + + AuthorizationSet finish_out_params; + string ciphertext; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */, + &finish_out_params, &ciphertext)); + + params = AuthorizationSetBuilder() + .Authorizations(begin_out_params) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 96); + + // Decrypt. + EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::DECRYPT, params)); +} + +/* + * EncryptionOperationsTest.AesGcmCorruptKey + * + * Verifies that AES GCM mode fails correctly when the decryption key is incorrect. + */ +TEST_F(EncryptionOperationsTest, AesGcmCorruptKey) { + string nonce = { + 0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f, + }; + string ciphertext = { + 0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16, + 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a, + 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76, + 0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd, + 0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0, + }; + + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128) + .Authorization(TAG_NONCE, nonce.data(), nonce.size()); + + auto import_params = AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_CALLER_NONCE) + .Authorization(TAG_MIN_MAC_LENGTH, 128); + + // Import correct key and decrypt + string key = { + 0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d, + 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb, + }; + ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key)); + string plaintext = DecryptMessage(ciphertext, params); + EXPECT_EQ(ErrorCode::OK, DeleteKey()); + + // Corrupt key and attempt to decrypt + key[0] = 0; + ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key)); + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext)); + EXPECT_EQ(ErrorCode::OK, DeleteKey()); +} + +/* + * EncryptionOperationsTest.AesGcmAadNoData + * + * Verifies that AES GCM mode works when provided additional authenticated data, but no data to + * encrypt. + */ +TEST_F(EncryptionOperationsTest, AesGcmAadNoData) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string aad = "1234567890123456"; + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto finish_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params)); + string ciphertext; + AuthorizationSet finish_out_params; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, "" /* input */, "" /* signature */, + &finish_out_params, &ciphertext)); + EXPECT_TRUE(finish_out_params.empty()); + + // Grab nonce + params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string plaintext; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, ciphertext, "" /* signature */, + &finish_out_params, &plaintext)) + << "(Possibly b/33615032)"; + + EXPECT_TRUE(finish_out_params.empty()); + + EXPECT_EQ("", plaintext); +} + +/* + * EncryptionOperationsTest.AesGcmMultiPartAad + * + * Verifies that AES GCM mode works when provided additional authenticated data in multiple chunks. + */ +TEST_F(EncryptionOperationsTest, AesGcmMultiPartAad) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string message = "123456789012345678901234567890123456"; + auto begin_params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + AuthorizationSet begin_out_params; + + auto update_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3); + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params)); + + // No data, AAD only. + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, "" /* input */, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(0U, input_consumed); + EXPECT_EQ(0U, ciphertext.size()); + EXPECT_TRUE(update_out_params.empty()); + + // AAD and data. + EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(message.size(), ciphertext.size()); + EXPECT_TRUE(update_out_params.empty()); + + EXPECT_EQ(ErrorCode::OK, Finish("" /* input */, &ciphertext)); + + // Grab nonce. + begin_params.push_back(begin_out_params); + + // Decrypt + update_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foofoo", (size_t)6); + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, update_params, ciphertext, "" /* signature */, + &update_out_params, &plaintext)); + EXPECT_TRUE(update_out_params.empty()); + EXPECT_EQ(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesGcmAadOutOfOrder + * + * Verifies that AES GCM mode fails correctly when given AAD after data to encipher. + */ +TEST_F(EncryptionOperationsTest, AesGcmAadOutOfOrder) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string message = "123456789012345678901234567890123456"; + auto begin_params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + AuthorizationSet begin_out_params; + + auto update_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3); + + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params)); + + // No data, AAD only. + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, "" /* input */, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(0U, input_consumed); + EXPECT_EQ(0U, ciphertext.size()); + EXPECT_TRUE(update_out_params.empty()); + + // AAD and data. + EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(message.size(), ciphertext.size()); + EXPECT_TRUE(update_out_params.empty()); + + // More AAD + EXPECT_EQ(ErrorCode::INVALID_TAG, Update(op_handle_, update_params, "", &update_out_params, + &ciphertext, &input_consumed)); + + op_handle_ = kOpHandleSentinel; +} + +/* + * EncryptionOperationsTest.AesGcmBadAad + * + * Verifies that AES GCM decryption fails correctly when additional authenticated date is wrong. + */ +TEST_F(EncryptionOperationsTest, AesGcmBadAad) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string message = "12345678901234567890123456789012"; + auto begin_params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto finish_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + AuthorizationSet finish_out_params; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */, + &finish_out_params, &ciphertext)); + + // Grab nonce + begin_params.push_back(begin_out_params); + + finish_params = AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, + "barfoo" /* Wrong AAD */, (size_t)6); + + // Decrypt. + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params)); + string plaintext; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, + Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params, + &plaintext)); +} + +/* + * EncryptionOperationsTest.AesGcmWrongNonce + * + * Verifies that AES GCM decryption fails correctly when the nonce is incorrect. + */ +TEST_F(EncryptionOperationsTest, AesGcmWrongNonce) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string message = "12345678901234567890123456789012"; + auto begin_params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto finish_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + AuthorizationSet finish_out_params; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */, + &finish_out_params, &ciphertext)); + + // Wrong nonce + begin_params.push_back(TAG_NONCE, HidlBuf("123456789012")); + + // Decrypt. + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params)); + string plaintext; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, + Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params, + &plaintext)); + + // With wrong nonce, should have gotten garbage plaintext (or none). + EXPECT_NE(message, plaintext); +} + +/* + * EncryptionOperationsTest.AesGcmCorruptTag + * + * Verifies that AES GCM decryption fails correctly when the tag is wrong. + */ +TEST_F(EncryptionOperationsTest, AesGcmCorruptTag) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + string aad = "1234567890123456"; + string message = "123456789012345678901234567890123456"; + + auto params = AuthorizationSetBuilder() + .BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, 128); + + auto finish_params = + AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params)); + string ciphertext; + AuthorizationSet finish_out_params; + EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */, + &finish_out_params, &ciphertext)); + EXPECT_TRUE(finish_out_params.empty()); + + // Corrupt tag + ++(*ciphertext.rbegin()); + + // Grab nonce + params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params)); + string plaintext; + EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, + Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params, + &plaintext)); + EXPECT_TRUE(finish_out_params.empty()); +} + +typedef KeymasterHidlTest MaxOperationsTest; + +/* + * MaxOperationsTest.TestLimitAes + * + * Verifies that the max uses per boot tag works correctly with AES keys. + */ +TEST_F(MaxOperationsTest, TestLimitAes) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .EcbMode() + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAX_USES_PER_BOOT, 3))); + + string message = "1234567890123456"; + + auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE); + + EncryptMessage(message, params); + EncryptMessage(message, params); + EncryptMessage(message, params); + + // Fourth time should fail. + EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::ENCRYPT, params)); +} + +/* + * MaxOperationsTest.TestLimitAes + * + * Verifies that the max uses per boot tag works correctly with RSA keys. + */ +TEST_F(MaxOperationsTest, TestLimitRsa) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 3) + .NoDigestOrPadding() + .Authorization(TAG_MAX_USES_PER_BOOT, 3))); + + string message = "1234567890123456"; + + auto params = AuthorizationSetBuilder().NoDigestOrPadding(); + + SignMessage(message, params); + SignMessage(message, params); + SignMessage(message, params); + + // Fourth time should fail. + EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params)); +} + +typedef KeymasterHidlTest AddEntropyTest; + +/* + * AddEntropyTest.AddEntropy + * + * Verifies that the addRngEntropy method doesn't blow up. There's no way to test that entropy is + * actually added. + */ +TEST_F(AddEntropyTest, AddEntropy) { + EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf("foo"))); +} + +/* + * AddEntropyTest.AddEmptyEntropy + * + * Verifies that the addRngEntropy method doesn't blow up when given an empty buffer. + */ +TEST_F(AddEntropyTest, AddEmptyEntropy) { + EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf())); +} + +/* + * AddEntropyTest.AddLargeEntropy + * + * Verifies that the addRngEntropy method doesn't blow up when given a largish amount of data. + */ +TEST_F(AddEntropyTest, AddLargeEntropy) { + EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf(string(16 * 1024, 'a')))); +} + +typedef KeymasterHidlTest AttestationTest; + +/* + * AttestationTest.RsaAttestation + * + * Verifies that attesting to RSA keys works and generates the expected output. + */ +TEST_F(AttestationTest, RsaAttestation) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .RsaSigningKey(1024, 3) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .Authorization(TAG_INCLUDE_UNIQUE_ID))); + + hidl_vec> cert_chain; + EXPECT_EQ(ErrorCode::OK, AttestKey(AuthorizationSetBuilder().Authorization( + TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge")), + &cert_chain)); + EXPECT_GE(cert_chain.size(), 2U); + EXPECT_TRUE(verify_chain(cert_chain)); + EXPECT_TRUE(verify_attestation_record("challenge", // + key_characteristics_.softwareEnforced, // + key_characteristics_.teeEnforced, // + cert_chain[0])); +} + +/* + * AttestationTest.EcAttestation + * + * Verifies that attesting to EC keys works and generates the expected output. + */ +TEST_F(AttestationTest, EcAttestation) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::SHA_2_256) + .Authorization(TAG_INCLUDE_UNIQUE_ID))); + + hidl_vec> cert_chain; + EXPECT_EQ(ErrorCode::OK, AttestKey(AuthorizationSetBuilder().Authorization( + TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge")), + &cert_chain)); + EXPECT_GE(cert_chain.size(), 2U); + EXPECT_TRUE(verify_chain(cert_chain)); + + EXPECT_TRUE(verify_attestation_record("challenge", // + key_characteristics_.softwareEnforced, // + key_characteristics_.teeEnforced, // + cert_chain[0])); +} + +/* + * AttestationTest.AesAttestation + * + * Verifies that attesting to AES keys fails in the expected way. + */ +TEST_F(AttestationTest, AesAttestation) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .AesEncryptionKey(128) + .EcbMode() + .Padding(PaddingMode::PKCS7))); + + hidl_vec> cert_chain; + EXPECT_EQ(ErrorCode::INCOMPATIBLE_ALGORITHM, + AttestKey(AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_CHALLENGE, + HidlBuf("challenge")), + &cert_chain)); +} + +/* + * AttestationTest.HmacAttestation + * + * Verifies that attesting to HMAC keys fails in the expected way. + */ +TEST_F(AttestationTest, HmacAttestation) { + ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .HmacKey(128) + .EcbMode() + .Digest(Digest::SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + hidl_vec> cert_chain; + EXPECT_EQ(ErrorCode::INCOMPATIBLE_ALGORITHM, + AttestKey(AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_CHALLENGE, + HidlBuf("challenge")), + &cert_chain)); +} + +} // namespace test +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + if (argc == 2) { + ALOGI("Running keymaster VTS against service \"%s\"", argv[1]); + service_name = argv[1]; + } + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; +} diff --git a/keymaster/3.0/vts/functional/keymaster_tags.h b/keymaster/3.0/vts/functional/keymaster_tags.h new file mode 100644 index 00000000..f241ef16 --- /dev/null +++ b/keymaster/3.0/vts/functional/keymaster_tags.h @@ -0,0 +1,450 @@ +/* + * Copyright 2014 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_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_ +#define SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_ + +/** + * This header contains various definitions that make working with keymaster tags safer and easier. + * + * It makes use of a fair amount of template metaprogramming. The metaprogramming serves the purpose + * of making it impossible to make certain classes of mistakes when operating on keymaster + * authorizations. For example, it's an error to create a KeyParameter with tag == Tag::PURPOSE + * and then to assign Algorithm::RSA to algorithm element of its union. But because the user + * must choose the union field, there could be a mismatch which the compiler has now way to + * diagnose. + * + * The machinery in this header solves these problems by describing which union field corresponds + * to which Tag. Central to this mechanism is the template TypedTag. It has zero size and binds a + * numeric Tag to a type that the compiler understands. By means of the macro DECLARE_TYPED_TAG, + * we declare types for each of the tags defined in hardware/interfaces/keymaster/2.0/types.hal. + * + * The macro DECLARE_TYPED_TAG(name) generates a typename TAG_name_t and a zero sized instance + * TAG_name. Once these typed tags have been declared we define metafunctions mapping the each tag + * to its value c++ type and the correct union element of KeyParameter. This is done by means of + * the macros MAKE_TAG_*VALUE_ACCESSOR, which generates TypedTag2ValueType, a metafunction mapping + * a typed tag to the corresponding c++ type, and access function, accessTagValue returning a + * reference to the correct element of KeyParameter. + * E.g.: + * given "KeyParameter param;" then "accessTagValue(TAG_PURPOSE, param)" + * yields a reference to param.f.purpose + * If used in an assignment the compiler can now check the compatibility of the assigned value. + * + * For convenience we also provide the constructor like function Authorization(). + * Authorization takes a typed tag and a value and checks at compile time whether the value given + * is suitable for the given tag. At runtime it creates a new KeyParameter initialized with the + * given tag and value and returns it by value. + * + * The second convenience function, authorizationValue, allows access to the KeyParameter value in + * a safe way. It takes a typed tag and a KeyParameter and returns a reference to the value wrapped + * by NullOr. NullOr has out-of-band information about whether it is save to access the wrapped + * reference. + * E.g.: + * auto param = Authorization(TAG_ALGORITM, Algorithm::RSA); + * auto value1 = authorizationValue(TAG_PURPOSE, param); + * auto value2 = authorizationValue(TAG_ALGORITM, param); + * value1.isOk() yields false, but value2.isOk() yields true, thus value2.value() is save to access. + */ + +#include +#include +#include + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have. We +// need these old values to be able to support old keys that use them. +static const int32_t KM_TAG_DIGEST_OLD = static_cast(TagType::ENUM) | 5; +static const int32_t KM_TAG_PADDING_OLD = static_cast(TagType::ENUM) | 7; + +constexpr TagType typeFromTag(Tag tag) { + return static_cast(static_cast(tag) & static_cast(0xf0000000)); +} + +/** + * TypedTag is a templatized version of Tag, which provides compile-time checking of keymaster tag + * types. Instances are convertible to Tag, so they can be used wherever Tag is expected, and + * because they encode the tag type it's possible to create function overloads that only operate on + * tags with a particular type. + */ +template struct TypedTag { + inline TypedTag() { + // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type + // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile + // error (no match for template specialization StaticAssert), with no run-time cost. + static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type"); + } + constexpr operator Tag() { return tag; } + constexpr long maskedTag() { + return static_cast(static_cast(tag) & static_cast(0x0fffffff)); + } +}; + +template struct Tag2TypedTag { typedef TypedTag type; }; + +template struct Tag2String; + +#define _TAGS_STRINGIFY(x) #x +#define TAGS_STRINGIFY(x) _TAGS_STRINGIFY(x) + +#define DECLARE_TYPED_TAG(name) \ + typedef typename Tag2TypedTag::type TAG_##name##_t; \ + extern TAG_##name##_t TAG_##name; \ + template <> struct Tag2String { \ + static const char* value() { return "Tag::" TAGS_STRINGIFY(name); } \ + } + +DECLARE_TYPED_TAG(INVALID); +DECLARE_TYPED_TAG(KEY_SIZE); +DECLARE_TYPED_TAG(MAC_LENGTH); +DECLARE_TYPED_TAG(CALLER_NONCE); +DECLARE_TYPED_TAG(MIN_MAC_LENGTH); +DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT); +DECLARE_TYPED_TAG(ECIES_SINGLE_HASH_MODE); +DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID); +DECLARE_TYPED_TAG(ACTIVE_DATETIME); +DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME); +DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME); +DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS); +DECLARE_TYPED_TAG(MAX_USES_PER_BOOT); +DECLARE_TYPED_TAG(ALL_USERS); +DECLARE_TYPED_TAG(USER_ID); +DECLARE_TYPED_TAG(USER_SECURE_ID); +DECLARE_TYPED_TAG(NO_AUTH_REQUIRED); +DECLARE_TYPED_TAG(AUTH_TIMEOUT); +DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY); +DECLARE_TYPED_TAG(ALL_APPLICATIONS); +DECLARE_TYPED_TAG(APPLICATION_ID); +DECLARE_TYPED_TAG(APPLICATION_DATA); +DECLARE_TYPED_TAG(CREATION_DATETIME); +DECLARE_TYPED_TAG(ROLLBACK_RESISTANT); +DECLARE_TYPED_TAG(ROOT_OF_TRUST); +DECLARE_TYPED_TAG(ASSOCIATED_DATA); +DECLARE_TYPED_TAG(NONCE); +DECLARE_TYPED_TAG(AUTH_TOKEN); +DECLARE_TYPED_TAG(BOOTLOADER_ONLY); +DECLARE_TYPED_TAG(OS_VERSION); +DECLARE_TYPED_TAG(OS_PATCHLEVEL); +DECLARE_TYPED_TAG(UNIQUE_ID); +DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE); +DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID); +DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION); + +DECLARE_TYPED_TAG(PURPOSE); +DECLARE_TYPED_TAG(ALGORITHM); +DECLARE_TYPED_TAG(BLOCK_MODE); +DECLARE_TYPED_TAG(DIGEST); +DECLARE_TYPED_TAG(PADDING); +DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS); +DECLARE_TYPED_TAG(ORIGIN); +DECLARE_TYPED_TAG(USER_AUTH_TYPE); +DECLARE_TYPED_TAG(KDF); +DECLARE_TYPED_TAG(EC_CURVE); + +template struct MetaList {}; + +using all_tags_t = MetaList< + TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t, + TAG_RSA_PUBLIC_EXPONENT_t, TAG_ECIES_SINGLE_HASH_MODE_t, TAG_INCLUDE_UNIQUE_ID_t, + TAG_ACTIVE_DATETIME_t, TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t, + TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_ALL_USERS_t, TAG_USER_ID_t, + TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t, + TAG_ALL_APPLICATIONS_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t, + TAG_ROLLBACK_RESISTANT_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, + TAG_AUTH_TOKEN_t, TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, + TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t, TAG_RESET_SINCE_ID_ROTATION_t, + TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t, TAG_DIGEST_t, TAG_PADDING_t, + TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_KDF_t, TAG_EC_CURVE_t>; + +/* implementation in keystore_utils.cpp */ +extern const char* stringifyTag(Tag tag); + +template struct TypedTag2ValueType; + +#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name) \ + template struct TypedTag2ValueType> { \ + typedef decltype(static_cast(nullptr)->field_name) type; \ + }; \ + template \ + inline auto accessTagValue(TypedTag, const KeyParameter& param) \ + ->const decltype(param.field_name)& { \ + return param.field_name; \ + } \ + template \ + inline auto accessTagValue(TypedTag, KeyParameter& param) \ + ->decltype(param.field_name)& { \ + return param.field_name; \ + } + +MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, f.longInteger) +MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, f.longInteger) +MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, f.dateTime) +MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, f.integer) +MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, f.integer) +MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, f.boolValue) +MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob) +MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob) + +#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name) \ + template <> struct TypedTag2ValueType { \ + typedef decltype(static_cast(nullptr)->field_name) type; \ + }; \ + inline auto accessTagValue(decltype(typed_tag), const KeyParameter& param) \ + ->const decltype(param.field_name)& { \ + return param.field_name; \ + } \ + inline auto accessTagValue(decltype(typed_tag), KeyParameter& param) \ + ->decltype(param.field_name)& { \ + return param.field_name; \ + } + +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, f.algorithm) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOB_USAGE_REQUIREMENTS, f.keyBlobUsageRequirements) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, f.blockMode) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, f.digest) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, f.ecCurve) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_KDF, f.keyDerivationFunction) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, f.origin) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, f.paddingMode) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, f.purpose) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, f.hardwareAuthenticatorType) + +template +inline KeyParameter makeKeyParameter(TypedTag ttag, ValueT&& value) { + KeyParameter param; + param.tag = tag; + param.f.longInteger = 0; + accessTagValue(ttag, param) = std::forward(value); + return param; +} + +// the boolean case +template inline KeyParameter makeKeyParameter(TypedTag) { + KeyParameter param; + param.tag = tag; + param.f.boolValue = true; + return param; +} + +template struct FirstOrNoneHelper; +template struct FirstOrNoneHelper { typedef First type; }; +template <> struct FirstOrNoneHelper<> { + struct type {}; +}; + +template using FirstOrNone = typename FirstOrNoneHelper::type; + +template +inline KeyParameter Authorization(TypedTag ttag, Args&&... args) { + static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0), + "TagType::BOOL Authorizations do not take parameters. Presence is truth."); + static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1), + "Authorization other then TagType::BOOL take exactly one parameter."); + static_assert( + tag_type == TagType::BOOL || + std::is_convertible>>, + typename TypedTag2ValueType>::type>::value, + "Invalid argument type for given tag."); + + return makeKeyParameter(ttag, std::forward(args)...); +} + +/** + * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out + * of band. Note that if the wrapped value is a reference it is unsafe to access the value if + * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the + * wrapped value. In this case the pointer will be NULL though, and the value will be default + * constructed. + */ +template class NullOr { + template struct reference_initializer { + static T&& init() { return *static_cast*>(nullptr); } + }; + template struct pointer_initializer { + static T init() { return nullptr; } + }; + template struct value_initializer { + static T init() { return T(); } + }; + template + using initializer_t = + std::conditional_t::value, reference_initializer, + std::conditional_t::value, pointer_initializer, + value_initializer>>; + + public: + NullOr() : value_(initializer_t::init()), null_(true) {} + NullOr(ValueT&& value) : value_(std::forward(value)), null_(false) {} + + bool isOk() const { return !null_; } + + const ValueT& value() const & { return value_; } + ValueT& value() & { return value_; } + ValueT&& value() && { return std::move(value_); } + + private: + ValueT value_; + bool null_; +}; + +template std::remove_reference_t NullOrOr(NullOr&& v) { + if (v.isOk()) return v; + return {}; +} + +template +std::remove_reference_t NullOrOr(Head&& head, Tail&&... tail) { + if (head.isOk()) return head; + return NullOrOr(std::forward(tail)...); +} + +template +std::remove_reference_t defaultOr(NullOr&& optional, Default&& def) { + static_assert(std::is_convertible, + std::remove_reference_t>::value, + "Type of default value must match the type wrapped by NullOr"); + if (optional.isOk()) return optional.value(); + return def; +} + +template +inline NullOr>::type&> +authorizationValue(TypedTag ttag, const KeyParameter& param) { + if (tag != param.tag) return {}; + return accessTagValue(ttag, param); +} + +inline const char* stringify(Digest digest) { + switch (digest) { + case Digest::NONE: + return "None"; + case Digest::MD5: + return "Md5"; + case Digest::SHA1: + return "Sha1"; + case Digest::SHA_2_224: + return "Sha224"; + case Digest::SHA_2_256: + return "Sha256"; + case Digest::SHA_2_384: + return "Sha384"; + case Digest::SHA_2_512: + return "Sha512"; + } + return "UNKNOWN DIGEST!"; +} + +inline const char* stringify(Algorithm algorithm) { + switch (algorithm) { + case Algorithm::RSA: + return "Rsa"; + case Algorithm::EC: + return "Ec"; + case Algorithm::AES: + return "Aes"; + case Algorithm::HMAC: + return "Hmac"; + } + return "UNKNOWN ALGORITHM"; +} + +inline const char* stringify(BlockMode block_mode) { + switch (block_mode) { + case BlockMode::ECB: + return "Ecb"; + case BlockMode::CBC: + return "Cbc"; + case BlockMode::CTR: + return "Ctr"; + case BlockMode::GCM: + return "Gcm"; + } + return "UNKNOWN BLOCK MODE"; +} + +inline const char* stringify(PaddingMode padding) { + switch (padding) { + case PaddingMode::NONE: + return "None"; + case PaddingMode::RSA_OAEP: + return "RsaOaep"; + case PaddingMode::RSA_PSS: + return "RsaPss"; + case PaddingMode::RSA_PKCS1_1_5_ENCRYPT: + return "RsaPkcs115Encrypt"; + case PaddingMode::RSA_PKCS1_1_5_SIGN: + return "RsaPkcs115Sign"; + case PaddingMode::PKCS7: + return "Pkcs7"; + } + return "UNKNOWN PADDING MODE"; +} + +inline const char* stringify(KeyOrigin origin) { + switch (origin) { + case KeyOrigin::GENERATED: + return "Generated"; + case KeyOrigin::DERIVED: + return "Derived"; + case KeyOrigin::IMPORTED: + return "Imported"; + case KeyOrigin::UNKNOWN: + return "UNKNOWN (keymaster0 didn't record it)"; + } + return "UNKOWN KEY ORIGIN VALUE"; +} + +inline const char* stringify(KeyPurpose purpose) { + switch (purpose) { + case KeyPurpose::ENCRYPT: + return "Encrypt"; + case KeyPurpose::DECRYPT: + return "Decrypt"; + case KeyPurpose::SIGN: + return "Sign"; + case KeyPurpose::VERIFY: + return "Verify"; + case KeyPurpose::DERIVE_KEY: + return "DeriveKey"; + case KeyPurpose::WRAP_KEY: + return "WrapKey"; + }; + return "UNKNOWN KEY PURPOSE"; +} + +inline const char* stringify(EcCurve curve) { + switch (curve) { + case EcCurve::P_224: + return "P_224"; + case EcCurve::P_256: + return "P_256"; + case EcCurve::P_384: + return "P_384"; + case EcCurve::P_521: + return "P_521"; + } + return "UNKNOWN EC CURVE"; +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android + +#endif // SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_ diff --git a/keymaster/3.0/vts/functional/keystore_tags_utils.cpp b/keymaster/3.0/vts/functional/keystore_tags_utils.cpp new file mode 100644 index 00000000..8dd99dba --- /dev/null +++ b/keymaster/3.0/vts/functional/keystore_tags_utils.cpp @@ -0,0 +1,50 @@ +/* +** +** Copyright 2016, 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_tags.h" + +namespace android { +namespace hardware { +namespace keymaster { +namespace V3_0 { + +template struct TagStringifier; + +template struct TagStringifier> { + template + static TypedTag chooseString(TypedTag ttag, Tag runtime_tag, + const char** result) { + if (tag == runtime_tag) { + *result = Tag2String::value(); + } + return ttag; + } + static const char* stringify(Tag tag) { + const char* result = "unknown tag"; + [](Tags&&...) {}(chooseString(Tags(), tag, &result)...); + return result; + } +}; + +const char* stringifyTag(Tag tag) { + return TagStringifier::stringify(tag); +} + +} // namespace V3_0 +} // namespace keymaster +} // namespace hardware +} // namespace android diff --git a/keymaster/3.0/vts/functional/openssl_utils.h b/keymaster/3.0/vts/functional/openssl_utils.h new file mode 100644 index 00000000..2eba9baa --- /dev/null +++ b/keymaster/3.0/vts/functional/openssl_utils.h @@ -0,0 +1,52 @@ +/* + * Copyright 2016 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. + */ + +template struct UniquePtrDeleter { + void operator()(T* p) const { F(p); } +}; + +typedef UniquePtrDeleter EVP_PKEY_Delete; + +#define MAKE_OPENSSL_PTR_TYPE(type) \ + typedef std::unique_ptr> type##_Ptr; + +MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT) +MAKE_OPENSSL_PTR_TYPE(EVP_PKEY) +MAKE_OPENSSL_PTR_TYPE(RSA) +MAKE_OPENSSL_PTR_TYPE(X509) +MAKE_OPENSSL_PTR_TYPE(BN_CTX) + +typedef std::unique_ptr> BIGNUM_Ptr; + +inline const EVP_MD* openssl_digest(android::hardware::keymaster::V3_0::Digest digest) { + switch (digest) { + case android::hardware::keymaster::V3_0::Digest::NONE: + return nullptr; + case android::hardware::keymaster::V3_0::Digest::MD5: + return EVP_md5(); + case android::hardware::keymaster::V3_0::Digest::SHA1: + return EVP_sha1(); + case android::hardware::keymaster::V3_0::Digest::SHA_2_224: + return EVP_sha224(); + case android::hardware::keymaster::V3_0::Digest::SHA_2_256: + return EVP_sha256(); + case android::hardware::keymaster::V3_0::Digest::SHA_2_384: + return EVP_sha384(); + case android::hardware::keymaster::V3_0::Digest::SHA_2_512: + return EVP_sha512(); + } + return nullptr; +} -- 2.11.0