| /* |
| * Copyright 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "import_wrapped_key.h" |
| #include "macros.h" |
| #include "proto_utils.h" |
| |
| #include <android-base/logging.h> |
| |
| #include <openssl/bytestring.h> |
| #include <openssl/ec.h> |
| #include <openssl/evp.h> |
| #include <openssl/mem.h> |
| #include <openssl/rsa.h> |
| #include <openssl/pkcs8.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace keymaster { |
| |
| // HAL |
| using ::android::hardware::keymaster::V4_0::Algorithm; |
| using ::android::hardware::keymaster::V4_0::BlockMode; |
| using ::android::hardware::keymaster::V4_0::Digest; |
| using ::android::hardware::keymaster::V4_0::EcCurve; |
| using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; |
| using ::android::hardware::keymaster::V4_0::KeyFormat; |
| using ::android::hardware::keymaster::V4_0::KeyPurpose; |
| using ::android::hardware::keymaster::V4_0::PaddingMode; |
| using ::android::hardware::keymaster::V4_0::Tag; |
| using ::android::hardware::keymaster::V4_0::TagType; |
| |
| // BoringSSL |
| using bssl::UniquePtr; |
| |
| // std |
| using std::function; |
| using std::unique_ptr; |
| |
| using parse_asn1_fn = function<ErrorCode(CBS *cbs, Tag tag, |
| ImportWrappedKeyRequest *request)>; |
| |
| #define KM_WRAPPER_FORMAT_VERSION 0 |
| #define KM_WRAPPER_GCM_IV_SIZE 12 |
| #define KM_WRAPPER_GCM_TAG_SIZE 16 |
| #define KM_WRAPPER_WRAPPED_AES_KEY_SIZE 32 |
| #define KM_WRAPPER_WRAPPED_DES_KEY_SIZE 24 |
| // TODO: update max once PKCS8 support is introduced. |
| #define KM_WRAPPER_WRAPPED_MAX_KEY_SIZE 32 |
| #define KM_TAG_MASK 0x0FFFFFFF |
| |
| // BoringSSL helpers. |
| static int CBS_get_optional_asn1_set(CBS *cbs, CBS *out, int *out_present, |
| unsigned tag) { |
| CBS child; |
| int present; |
| if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { |
| return 0; |
| } |
| if (present) { |
| if (!CBS_get_asn1(&child, out, CBS_ASN1_SET) || |
| CBS_len(&child) != 0) { |
| return 0; |
| } |
| } else { |
| CBS_init(out, NULL, 0); |
| } |
| if (out_present) { |
| *out_present = present; |
| } |
| return 1; |
| } |
| |
| static int CBS_get_optional_asn1_null(CBS *cbs, CBS *out, int *out_present, |
| unsigned tag) { |
| CBS child; |
| int present; |
| if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { |
| return 0; |
| } |
| if (present) { |
| if (!CBS_get_asn1(&child, out, CBS_ASN1_NULL) || |
| CBS_len(&child) != 0) { |
| return 0; |
| } |
| } else { |
| CBS_init(out, NULL, 0); |
| } |
| if (out_present) { |
| *out_present = present; |
| } |
| return 1; |
| } |
| |
| static ErrorCode parse_asn1_set(CBS *auth, Tag tag, |
| ImportWrappedKeyRequest *request) |
| { |
| CBS set; |
| int present; |
| if (!CBS_get_optional_asn1_set( |
| auth, &set, &present, |
| CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | |
| (tag & 0x0fffffff))) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (!present) { |
| // This is optional parameter, so ok to be missing. |
| return ErrorCode::OK; |
| } |
| // TODO: are empty sets acceptable? |
| |
| while (CBS_len(&set)) { |
| uint64_t val; |
| KeyParameter kp; |
| if (!CBS_get_asn1_uint64(&set, &val)) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| kp.tag = tag; |
| switch (tag) { |
| case Tag::PURPOSE: |
| kp.f.purpose = (KeyPurpose)val; |
| break; |
| case Tag::BLOCK_MODE: |
| kp.f.blockMode = (BlockMode)val; |
| break; |
| case Tag::DIGEST: |
| kp.f.digest = (Digest)val; |
| break; |
| case Tag::PADDING: |
| kp.f.paddingMode = (PaddingMode)val; |
| break; |
| case Tag::USER_SECURE_ID: |
| kp.f.longInteger = val; |
| break; |
| default: |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| nosapp::KeyParameter *param = request->mutable_params()->add_params(); |
| if (key_parameter_to_pb(kp, param) != ErrorCode::OK) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| } |
| |
| return ErrorCode::OK; |
| } |
| |
| static ErrorCode parse_asn1_integer(CBS *auth, Tag tag, |
| ImportWrappedKeyRequest *request) |
| { |
| uint64_t val; |
| if (!CBS_get_optional_asn1_uint64( |
| auth, &val, |
| CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | |
| (tag & 0x0fffffff), (uint64_t)-1)) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (val == (uint64_t)-1) { |
| // This is optional parameter, so ok to be missing. |
| return ErrorCode::OK; |
| } |
| |
| KeyParameter kp; |
| kp.tag = tag; |
| switch (tag) { |
| case Tag::ALGORITHM: |
| kp.f.algorithm = (Algorithm)val; |
| break; |
| case Tag::KEY_SIZE: |
| case Tag::MIN_MAC_LENGTH: |
| case Tag::RSA_PUBLIC_EXPONENT: |
| case Tag::MIN_SECONDS_BETWEEN_OPS: |
| case Tag::MAX_USES_PER_BOOT: |
| case Tag::AUTH_TIMEOUT: |
| if (val > UINT32_MAX) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| kp.f.integer = (uint32_t)val; |
| break; |
| case Tag::EC_CURVE: |
| kp.f.ecCurve = (EcCurve)val; |
| break; |
| case Tag::ACTIVE_DATETIME: |
| case Tag::ORIGINATION_EXPIRE_DATETIME: |
| case Tag::USAGE_EXPIRE_DATETIME: |
| case Tag::CREATION_DATETIME: |
| kp.f.longInteger = val; |
| break; |
| case Tag::USER_AUTH_TYPE: |
| kp.f.hardwareAuthenticatorType = (HardwareAuthenticatorType)val; |
| break; |
| default: |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| nosapp::KeyParameter *param = request->mutable_params()->add_params(); |
| if (key_parameter_to_pb(kp, param) != ErrorCode::OK) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| return ErrorCode::OK; |
| } |
| |
| static ErrorCode parse_asn1_boolean(CBS *auth, Tag tag, |
| ImportWrappedKeyRequest *request) |
| { |
| CBS null; |
| int present; |
| if (!CBS_get_optional_asn1_null( |
| auth, &null, &present, |
| CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | |
| (tag & 0x0fffffff))) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (!present) { |
| // This is optional parameter, so ok to be missing. |
| return ErrorCode::OK; |
| } |
| if (CBS_len(&null) != 0) { |
| // NULL type should be empty. |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| KeyParameter kp; |
| kp.tag = tag; |
| switch (tag) { |
| case Tag::CALLER_NONCE: |
| case Tag::BOOTLOADER_ONLY: |
| case Tag::NO_AUTH_REQUIRED: |
| kp.f.boolValue = true; |
| break; |
| default: |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| nosapp::KeyParameter *param = request->mutable_params()->add_params(); |
| if (key_parameter_to_pb(kp, param) != ErrorCode::OK) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| return ErrorCode::OK; |
| } |
| |
| static ErrorCode parse_asn1_octet_string(CBS *auth, Tag tag, |
| ImportWrappedKeyRequest *request) |
| { |
| CBS str; |
| int present; |
| if (!CBS_get_optional_asn1_octet_string( |
| auth, &str, &present, |
| CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | |
| (tag & 0x0fffffff))) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (!present) { |
| // This is optional parameter, so ok to be missing. |
| return ErrorCode::OK; |
| } |
| if (CBS_len(&str) == 0) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| KeyParameter kp; |
| kp.tag = tag; |
| switch (tag) { |
| case Tag::APPLICATION_DATA: |
| case Tag::APPLICATION_ID: |
| kp.blob.setToExternal( |
| const_cast<uint8_t*>(CBS_data(&str)), |
| CBS_len(&str), false); |
| break; |
| default: |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| nosapp::KeyParameter *param = request->mutable_params()->add_params(); |
| if (key_parameter_to_pb(kp, param) != ErrorCode::OK) { |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| return ErrorCode::OK; |
| } |
| |
| ErrorCode import_wrapped_key_request(const hidl_vec<uint8_t>& wrappedKeyData, |
| const hidl_vec<uint8_t>& wrappingKeyBlob, |
| const hidl_vec<uint8_t>& maskingKey, |
| ImportWrappedKeyRequest *request) |
| { |
| /* |
| * Unwrap an ASN.1 DER wrapped key, as specified here: |
| * https://docs.google.com/document/d/165Jd6jumhOD2yB6MygKhqjOLuHm3YVZGTgFHmGc8HzA/edit#heading=h.ut6j2przg4ra |
| * |
| * AuthorizationList ::= SEQUENCE { |
| * purpose [1] EXPLICIT SET OF INTEGER OPTIONAL, |
| * algorithm [2] EXPLICIT INTEGER OPTIONAL, |
| * keySize [3] EXPLICIT INTEGER OPTIONAL, |
| * blockMode [4] EXPLICIT SET OF INTEGER OPTIONAL, |
| * digest [5] EXPLICIT SET OF INTEGER OPTIONAL, |
| * padding [6] EXPLICIT SET OF INTEGER OPTIONAL, |
| * callerNonce [7] EXPLICIT NULL OPTIONAL, |
| * minMacLength [8] EXPLICIT INTEGER OPTIONAL, |
| * ecCurve [10] EXPLICIT INTEGER OPTIONAL, |
| * rsaPublicExponent [200] EXPLICIT INTEGER OPTIONAL, |
| * includeUniqueId [202] EXPLICIT NULL OPTIONAL, |
| * blobUsageRequirements [301] EXPLICIT INTEGER OPTIONAL, |
| * bootloaderOnly [302] EXPLICIT NULL OPTIONAL, |
| * rollbackResistance [303] EXPLICIT NULL OPTIONAL, |
| * activeDateTime [400] EXPLICIT INTEGER OPTIONAL, |
| * originationExpireDateTime [401] EXPLICIT INTEGER OPTIONAL, |
| * usageExpireDateTime [402] EXPLICIT INTEGER OPTIONAL, |
| * minSecondsBetweenOps [403] EXPLICIT INTEGER OPTIONAL, |
| * maxUsesPerBoot [404] EXPLICIT INTEGER OPTIONAL, |
| * userSecureId [502] EXPLICIT SET OF INTEGER OPTIONAL, |
| * noAuthRequired [503] EXPLICIT NULL OPTIONAL, |
| * userAuthType [504] EXPLICIT INTEGER OPTIONAL, |
| * authTimeout [505] EXPLICIT INTEGER OPTIONAL, |
| * trustedUserPresenceReq [507] EXPLICIT NULL OPTIONAL, |
| * trustedConfirmationReq [508] EXPLICIT NULL OPTIONAL, |
| * unlockedDeviceReq [509] EXPLICIT NULL OPTIONAL, |
| * applicationId [601] EXPLICIT OCTET_STRING OPTIONAL, |
| * applicationData [700] EXPLICIT OCTET_STRING OPTIONAL, |
| * creationDateTime [701] EXPLICIT INTEGER OPTIONAL, |
| * } |
| * |
| * KeyDescription ::= SEQUENCE { |
| * keyFormat INTEGER, |
| * authorizationList AuthorizationList |
| * } |
| * |
| * SecureKeyWrapper ::= SEQUENCE { |
| * wrapperFormatVersion INTEGER, |
| * encryptedTransportKey OCTET_STRING, |
| * initializationVector OCTET_STRING, |
| * keyDescription KeyDescription, |
| * secureKey OCTET_STRING, |
| * tag OCTET_STRING, |
| * } |
| */ |
| |
| CBS cbs; |
| CBS child; |
| uint64_t wrapperFormatVersion; |
| CBS encryptedTransportKey; |
| CBS initializationVector; |
| |
| CBS_init(&cbs, wrappedKeyData.data(), wrappedKeyData.size()); |
| if (!CBS_get_asn1(&cbs, &child, CBS_ASN1_SEQUENCE) || |
| !CBS_get_asn1_uint64(&child, &wrapperFormatVersion)) { |
| LOG(ERROR) << "Failed to wrap outer sequence or wrapperFormatVersion"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (wrapperFormatVersion != KM_WRAPPER_FORMAT_VERSION) { |
| LOG(ERROR) << "Invalid wrapper format version" << wrapperFormatVersion; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (!CBS_get_asn1(&child, &encryptedTransportKey, CBS_ASN1_OCTETSTRING) || |
| !CBS_get_asn1(&child, &initializationVector, CBS_ASN1_OCTETSTRING)) { |
| LOG(ERROR) << |
| "Failed to parse encryptedTransportKey or initializationVector"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| /* TODO: if the RSA key-size is known from the blob, use it to |
| * validate encryptedTransportKey (i.e. the RSA envelope) length. |
| */ |
| |
| if (CBS_len(&initializationVector) != KM_WRAPPER_GCM_IV_SIZE) { |
| LOG(ERROR) << "Invalid AAD length"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| CBS aad; |
| if (!CBS_get_asn1_element(&child, &aad, CBS_ASN1_SEQUENCE)) { |
| LOG(ERROR) << "Failed to parse aad"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| /* Assign over AAD before CBS gets consumed. */ |
| request->set_aad(CBS_data(&aad), CBS_len(&aad)); |
| |
| CBS keyDescription; |
| if (!CBS_get_asn1(&aad, &keyDescription, CBS_ASN1_SEQUENCE)) { |
| LOG(ERROR) << "Failed to parse keyDescription"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| uint64_t keyFormat; |
| if (!CBS_get_asn1_uint64(&keyDescription, &keyFormat)) { |
| LOG(ERROR) << "Failed to parse keyFormat"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| CBS authorizationList; |
| if (!CBS_get_asn1(&keyDescription, &authorizationList, CBS_ASN1_SEQUENCE) || |
| CBS_len(&keyDescription) != 0) { |
| LOG(ERROR) << "Failed to parse keyDescription, remaining length: " |
| << CBS_len(&keyDescription); |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| struct tag_parser_entry { |
| Tag tag; |
| const parse_asn1_fn fn; |
| }; |
| const struct tag_parser_entry parser_table[] = { |
| {Tag::PURPOSE, parse_asn1_set}, |
| {Tag::ALGORITHM, parse_asn1_integer}, |
| {Tag::KEY_SIZE, parse_asn1_integer}, |
| {Tag::BLOCK_MODE, parse_asn1_set}, |
| {Tag::DIGEST, parse_asn1_set}, |
| {Tag::PADDING, parse_asn1_set}, |
| {Tag::CALLER_NONCE, parse_asn1_boolean}, |
| {Tag::MIN_MAC_LENGTH, parse_asn1_integer}, |
| {Tag::EC_CURVE, parse_asn1_integer}, |
| {Tag::RSA_PUBLIC_EXPONENT, parse_asn1_integer}, |
| {Tag::INCLUDE_UNIQUE_ID, parse_asn1_boolean}, |
| {Tag::BLOB_USAGE_REQUIREMENTS, parse_asn1_integer}, |
| {Tag::BOOTLOADER_ONLY, parse_asn1_boolean}, |
| {Tag::ROLLBACK_RESISTANCE, parse_asn1_boolean}, |
| {Tag::ACTIVE_DATETIME, parse_asn1_integer}, |
| {Tag::ORIGINATION_EXPIRE_DATETIME, parse_asn1_integer}, |
| {Tag::USAGE_EXPIRE_DATETIME, parse_asn1_integer}, |
| {Tag::MIN_SECONDS_BETWEEN_OPS, parse_asn1_integer}, |
| {Tag::MAX_USES_PER_BOOT, parse_asn1_integer}, |
| {Tag::USER_SECURE_ID, parse_asn1_set}, |
| {Tag::NO_AUTH_REQUIRED, parse_asn1_boolean}, |
| {Tag::USER_AUTH_TYPE, parse_asn1_integer}, |
| {Tag::AUTH_TIMEOUT, parse_asn1_integer}, |
| {Tag::TRUSTED_USER_PRESENCE_REQUIRED, parse_asn1_boolean}, |
| {Tag::TRUSTED_CONFIRMATION_REQUIRED, parse_asn1_boolean}, |
| {Tag::UNLOCKED_DEVICE_REQUIRED, parse_asn1_boolean}, |
| {Tag::APPLICATION_ID, parse_asn1_octet_string}, |
| {Tag::APPLICATION_DATA, parse_asn1_octet_string}, |
| {Tag::CREATION_DATETIME, parse_asn1_octet_string}, |
| }; |
| |
| for (size_t i = 0; i < ARRAYSIZE(parser_table); i++) { |
| const struct tag_parser_entry *entry = &parser_table[i]; |
| |
| if (entry->fn(&authorizationList, entry->tag, request) |
| != ErrorCode::OK) { |
| LOG(ERROR) << |
| "ImportWrappedKey authorization list parse failure: tag: " |
| << (uint32_t)entry->tag; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| } |
| |
| if (CBS_len(&authorizationList) != 0) { |
| LOG(ERROR) << "Authorization list not fully consumed, " |
| << CBS_len(&authorizationList) |
| << " bytes remaining"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| CBS secureKey; |
| CBS tag; |
| if (!CBS_get_asn1(&child, &secureKey, CBS_ASN1_OCTETSTRING)) { |
| LOG(ERROR) << "Failed to parse secure key"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| // TODO: check that the wrapped key size matches the algorithm. |
| if (CBS_len(&secureKey) > KM_WRAPPER_WRAPPED_MAX_KEY_SIZE) { |
| LOG(ERROR) << "Secure key len exceeded: " |
| << KM_WRAPPER_WRAPPED_MAX_KEY_SIZE |
| << " got: " |
| << CBS_len(&secureKey); |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| if (!CBS_get_asn1(&child, &tag, CBS_ASN1_OCTETSTRING)) { |
| LOG(ERROR) << "Failed to parse gcm tag"; |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| if (CBS_len(&tag) != KM_WRAPPER_GCM_TAG_SIZE) { |
| LOG(ERROR) << "GCM tag len, expected: " |
| << KM_WRAPPER_GCM_TAG_SIZE |
| << " got: " |
| << CBS_len(&tag); |
| return ErrorCode::INVALID_ARGUMENT; |
| } |
| |
| request->set_key_format(keyFormat); |
| request->set_rsa_envelope(CBS_data(&encryptedTransportKey), |
| CBS_len(&encryptedTransportKey)); |
| request->set_initialization_vector(CBS_data(&initializationVector), |
| CBS_len(&initializationVector)); |
| request->set_encrypted_import_key(CBS_data(&secureKey), |
| CBS_len(&secureKey)); |
| request->set_gcm_tag(CBS_data(&tag), CBS_len(&tag)); |
| request->mutable_wrapping_key_blob()->set_blob(wrappingKeyBlob.data(), |
| wrappingKeyBlob.size()); |
| |
| request->set_masking_key(maskingKey.data(), maskingKey.size()); |
| |
| return ErrorCode::OK; |
| } |
| |
| } // namespace keymaster |
| } // hardware |
| } // android |