| /* |
| ** |
| ** Copyright 2017, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <keymaster/key_blob_utils/software_keyblobs.h> |
| |
| #include <stdint.h> |
| |
| #include <hardware/keymaster_defs.h> |
| |
| #include <keymaster/UniquePtr.h> |
| #include <keymaster/android_keymaster_utils.h> |
| #include <keymaster/authorization_set.h> |
| #include <keymaster/key.h> |
| #include <keymaster/key_blob_utils/auth_encrypted_key_blob.h> |
| #include <keymaster/key_blob_utils/integrity_assured_key_blob.h> |
| #include <keymaster/key_blob_utils/ocb_utils.h> |
| #include <keymaster/km_openssl/openssl_err.h> |
| #include <keymaster/km_openssl/openssl_utils.h> |
| #include <keymaster/logger.h> |
| |
| #include <openssl/aes.h> |
| |
| namespace keymaster { |
| |
| static uint8_t SWROT[2] = {'S', 'W'}; |
| KeymasterBlob softwareRootOfTrust(SWROT); |
| |
| namespace { |
| |
| bool UpgradeIntegerTag(keymaster_tag_t tag, uint32_t value, AuthorizationSet* set, |
| bool* set_changed) { |
| int index = set->find(tag); |
| if (index == -1) { |
| keymaster_key_param_t param; |
| param.tag = tag; |
| param.integer = value; |
| set->push_back(param); |
| *set_changed = true; |
| return true; |
| } |
| |
| if (set->params[index].integer > value) return false; |
| |
| if (set->params[index].integer != value) { |
| set->params[index].integer = value; |
| *set_changed = true; |
| } |
| return true; |
| } |
| |
| keymaster_error_t TranslateAuthorizationSetError(AuthorizationSet::Error err) { |
| switch (err) { |
| case AuthorizationSet::OK: |
| return KM_ERROR_OK; |
| case AuthorizationSet::ALLOCATION_FAILURE: |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| case AuthorizationSet::MALFORMED_DATA: |
| return KM_ERROR_UNKNOWN_ERROR; |
| } |
| return KM_ERROR_OK; |
| } |
| |
| } // anonymous namespace |
| |
| keymaster_error_t BuildHiddenAuthorizations(const AuthorizationSet& input_set, |
| AuthorizationSet* hidden, |
| const KeymasterBlob& root_of_trust) { |
| keymaster_blob_t entry; |
| if (input_set.GetTagValue(TAG_APPLICATION_ID, &entry)) |
| hidden->push_back(TAG_APPLICATION_ID, entry.data, entry.data_length); |
| if (input_set.GetTagValue(TAG_APPLICATION_DATA, &entry)) |
| hidden->push_back(TAG_APPLICATION_DATA, entry.data, entry.data_length); |
| |
| hidden->push_back(TAG_ROOT_OF_TRUST, root_of_trust); |
| |
| return TranslateAuthorizationSetError(hidden->is_valid()); |
| } |
| |
| keymaster_error_t FakeKeyAuthorizations(EVP_PKEY* pubkey, AuthorizationSet* hw_enforced, |
| AuthorizationSet* sw_enforced) { |
| hw_enforced->Clear(); |
| sw_enforced->Clear(); |
| |
| switch (EVP_PKEY_type(pubkey->type)) { |
| case EVP_PKEY_RSA: { |
| hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_NONE); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_MD5); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA1); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); |
| hw_enforced->push_back(TAG_PADDING, KM_PAD_NONE); |
| hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); |
| hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); |
| hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PSS); |
| hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_OAEP); |
| |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_DECRYPT); |
| |
| RSA_Ptr rsa(EVP_PKEY_get1_RSA(pubkey)); |
| if (!rsa) return TranslateLastOpenSslError(); |
| hw_enforced->push_back(TAG_KEY_SIZE, RSA_size(rsa.get()) * 8); |
| uint64_t public_exponent = BN_get_word(rsa->e); |
| if (public_exponent == 0xffffffffL) return KM_ERROR_INVALID_KEY_BLOB; |
| hw_enforced->push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent); |
| break; |
| } |
| |
| case EVP_PKEY_EC: { |
| hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_NONE); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_MD5); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA1); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); |
| hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); |
| |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); |
| sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); |
| |
| UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pubkey)); |
| if (!ec_key.get()) return TranslateLastOpenSslError(); |
| size_t key_size_bits; |
| keymaster_error_t error = |
| ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &key_size_bits); |
| if (error != KM_ERROR_OK) return error; |
| hw_enforced->push_back(TAG_KEY_SIZE, key_size_bits); |
| break; |
| } |
| |
| default: |
| return KM_ERROR_UNSUPPORTED_ALGORITHM; |
| } |
| |
| sw_enforced->push_back(TAG_ALL_USERS); |
| sw_enforced->push_back(TAG_NO_AUTH_REQUIRED); |
| |
| return KM_ERROR_OK; |
| } |
| |
| // Note: This parsing code in below is from system/security/softkeymaster/keymaster_openssl.cpp's |
| // unwrap_key function, modified for the preferred function signature and formatting. It does some |
| // odd things, but they have been left unchanged to avoid breaking compatibility. |
| static const uint8_t SOFT_KEY_MAGIC[] = {'P', 'K', '#', '8'}; |
| keymaster_error_t ParseOldSoftkeymasterBlob(const KeymasterKeyBlob& blob, |
| KeymasterKeyBlob* key_material, |
| AuthorizationSet* hw_enforced, |
| AuthorizationSet* sw_enforced) { |
| long publicLen = 0; // NOLINT(google-runtime-int) |
| long privateLen = 0; // NOLINT(google-runtime-int) |
| const uint8_t* p = blob.key_material; |
| const uint8_t* end = blob.key_material + blob.key_material_size; |
| |
| int type = 0; |
| ptrdiff_t min_size = |
| sizeof(SOFT_KEY_MAGIC) + sizeof(type) + sizeof(publicLen) + 1 + sizeof(privateLen) + 1; |
| if (end - p < min_size) { |
| LOG_W("key blob appears to be truncated (if an old SW key)", 0); |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| |
| if (memcmp(p, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC)) != 0) return KM_ERROR_INVALID_KEY_BLOB; |
| p += sizeof(SOFT_KEY_MAGIC); |
| |
| for (size_t i = 0; i < sizeof(type); i++) { |
| type = (type << 8) | *p++; |
| } |
| |
| for (size_t i = 0; i < sizeof(type); i++) { |
| publicLen = (publicLen << 8) | *p++; |
| } |
| |
| if (p + publicLen > end) { |
| LOG_W("public key length encoding error: size=%ld, end=%td", publicLen, end - p); |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| p += publicLen; |
| |
| if (end - p < 2) { |
| LOG_W("key blob appears to be truncated (if an old SW key)", 0); |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| |
| for (size_t i = 0; i < sizeof(type); i++) |
| privateLen = (privateLen << 8) | *p++; |
| |
| if (p + privateLen > end) { |
| LOG_W("private key length encoding error: size=%ld, end=%td", privateLen, end - p); |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| |
| // Just to be sure, make sure that the ASN.1 structure parses correctly. We don't actually use |
| // the EVP_PKEY here. |
| const uint8_t* key_start = p; |
| EVP_PKEY_Ptr pkey(d2i_PrivateKey(type, nullptr, &p, privateLen)); |
| if (pkey.get() == nullptr) { |
| LOG_W("Failed to parse PKCS#8 key material (if old SW key)", 0); |
| return KM_ERROR_INVALID_KEY_BLOB; |
| } |
| |
| // All auths go into sw_enforced, including those that would be HW-enforced if we were faking |
| // auths for a HW-backed key. |
| hw_enforced->Clear(); |
| keymaster_error_t error = FakeKeyAuthorizations(pkey.get(), sw_enforced, sw_enforced); |
| if (error != KM_ERROR_OK) return error; |
| |
| if (!key_material->Reset(privateLen)) return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| memcpy(key_material->writable_data(), key_start, privateLen); |
| |
| return KM_ERROR_OK; |
| } |
| |
| static uint8_t master_key_bytes[AES_BLOCK_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| const KeymasterKeyBlob MASTER_KEY(master_key_bytes, array_length(master_key_bytes)); |
| |
| keymaster_error_t ParseAuthEncryptedBlob(const KeymasterKeyBlob& blob, |
| const AuthorizationSet& hidden, |
| KeymasterKeyBlob* key_material, |
| AuthorizationSet* hw_enforced, |
| AuthorizationSet* sw_enforced) { |
| keymaster_error_t error; |
| DeserializedKey key = DeserializeAuthEncryptedBlob(blob, &error); |
| if (error != KM_ERROR_OK) return error; |
| |
| *key_material = DecryptKey(key, hidden, MASTER_KEY, &error); |
| *hw_enforced = move(key.hw_enforced); |
| *sw_enforced = move(key.sw_enforced); |
| return error; |
| } |
| |
| keymaster_error_t SetKeyBlobAuthorizations(const AuthorizationSet& key_description, |
| keymaster_key_origin_t origin, uint32_t os_version, |
| uint32_t os_patchlevel, AuthorizationSet* hw_enforced, |
| AuthorizationSet* sw_enforced) { |
| sw_enforced->Clear(); |
| |
| for (auto& entry : key_description) { |
| switch (entry.tag) { |
| // These cannot be specified by the client. |
| case KM_TAG_BOOT_PATCHLEVEL: |
| case KM_TAG_ORIGIN: |
| case KM_TAG_OS_PATCHLEVEL: |
| case KM_TAG_OS_VERSION: |
| case KM_TAG_ROOT_OF_TRUST: |
| case KM_TAG_VENDOR_PATCHLEVEL: |
| LOG_E("Root of trust and origin tags may not be specified", 0); |
| return KM_ERROR_INVALID_TAG; |
| |
| case KM_TAG_ALLOW_WHILE_ON_BODY: |
| // Not supported, but is specified to noop in that case (vs error). |
| LOG_W("No on-body detection supported, skipping tag %d", entry.tag); |
| break; |
| |
| // These aren't supported by SoftKeymaster. |
| case KM_TAG_DEVICE_UNIQUE_ATTESTATION: |
| case KM_TAG_ECIES_SINGLE_HASH_MODE: |
| case KM_TAG_EXPORTABLE: |
| case KM_TAG_IDENTITY_CREDENTIAL_KEY: |
| case KM_TAG_KDF: |
| case KM_TAG_ROLLBACK_RESISTANT: |
| case KM_TAG_STORAGE_KEY: |
| LOG_E("Tag %d not supported by SoftKeymaster", entry.tag); |
| return KM_ERROR_UNSUPPORTED_TAG; |
| |
| // If the hardware enforce list contains this tag, means we are |
| // pretending to be some secure hardware which has secure storage. |
| case KM_TAG_ROLLBACK_RESISTANCE: |
| if (hw_enforced->GetTagCount(entry.tag) != 0) |
| break; |
| else { |
| LOG_E("Tag %d not supported by SoftKeymaster", entry.tag); |
| return KM_ERROR_UNSUPPORTED_TAG; |
| } |
| |
| // These are hidden. |
| case KM_TAG_APPLICATION_DATA: |
| case KM_TAG_APPLICATION_ID: |
| break; |
| |
| // These should not be in key descriptions because they're for operation parameters. |
| case KM_TAG_ASSOCIATED_DATA: |
| case KM_TAG_AUTH_TOKEN: |
| case KM_TAG_CONFIRMATION_TOKEN: |
| case KM_TAG_INVALID: |
| case KM_TAG_MAC_LENGTH: |
| case KM_TAG_NONCE: |
| LOG_E("Tag %d not allowed in key generation/import", entry.tag); |
| break; |
| |
| // These are provided to support attesation key generation, but should not be included in |
| // the key characteristics. |
| case KM_TAG_ATTESTATION_APPLICATION_ID: |
| case KM_TAG_ATTESTATION_CHALLENGE: |
| case KM_TAG_ATTESTATION_ID_BRAND: |
| case KM_TAG_ATTESTATION_ID_DEVICE: |
| case KM_TAG_ATTESTATION_ID_IMEI: |
| case KM_TAG_ATTESTATION_ID_MANUFACTURER: |
| case KM_TAG_ATTESTATION_ID_MEID: |
| case KM_TAG_ATTESTATION_ID_MODEL: |
| case KM_TAG_ATTESTATION_ID_PRODUCT: |
| case KM_TAG_ATTESTATION_ID_SERIAL: |
| case KM_TAG_CERTIFICATE_SERIAL: |
| case KM_TAG_CERTIFICATE_SUBJECT: |
| case KM_TAG_CERTIFICATE_NOT_BEFORE: |
| case KM_TAG_CERTIFICATE_NOT_AFTER: |
| case KM_TAG_RESET_SINCE_ID_ROTATION: |
| break; |
| |
| // Everything else we just copy into sw_enforced, unless the KeyFactory has placed it in |
| // hw_enforced, in which case we defer to its decision. |
| case KM_TAG_ACTIVE_DATETIME: |
| case KM_TAG_ALGORITHM: |
| case KM_TAG_ALL_APPLICATIONS: |
| case KM_TAG_ALL_USERS: |
| case KM_TAG_AUTH_TIMEOUT: |
| case KM_TAG_BLOB_USAGE_REQUIREMENTS: |
| case KM_TAG_BLOCK_MODE: |
| case KM_TAG_BOOTLOADER_ONLY: |
| case KM_TAG_CALLER_NONCE: |
| case KM_TAG_CREATION_DATETIME: |
| case KM_TAG_DIGEST: |
| case KM_TAG_EARLY_BOOT_ONLY: |
| case KM_TAG_EC_CURVE: |
| case KM_TAG_INCLUDE_UNIQUE_ID: |
| case KM_TAG_KEY_SIZE: |
| case KM_TAG_MAX_BOOT_LEVEL: |
| case KM_TAG_MAX_USES_PER_BOOT: |
| case KM_TAG_MIN_MAC_LENGTH: |
| case KM_TAG_MIN_SECONDS_BETWEEN_OPS: |
| case KM_TAG_NO_AUTH_REQUIRED: |
| case KM_TAG_ORIGINATION_EXPIRE_DATETIME: |
| case KM_TAG_PADDING: |
| case KM_TAG_PURPOSE: |
| case KM_TAG_RSA_OAEP_MGF_DIGEST: |
| case KM_TAG_RSA_PUBLIC_EXPONENT: |
| case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED: |
| case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED: |
| case KM_TAG_UNIQUE_ID: |
| case KM_TAG_UNLOCKED_DEVICE_REQUIRED: |
| case KM_TAG_USAGE_COUNT_LIMIT: |
| case KM_TAG_USAGE_EXPIRE_DATETIME: |
| case KM_TAG_USER_AUTH_TYPE: |
| case KM_TAG_USER_ID: |
| case KM_TAG_USER_SECURE_ID: |
| if (hw_enforced->GetTagCount(entry.tag) == 0) sw_enforced->push_back(entry); |
| break; |
| } |
| } |
| |
| // If hw_enforced is non-empty, we're pretending to be some sort of secure hardware. |
| AuthorizationSet* pseudo_hw_enforced = (hw_enforced->empty()) ? sw_enforced : hw_enforced; |
| pseudo_hw_enforced->push_back(TAG_ORIGIN, origin); |
| pseudo_hw_enforced->push_back(TAG_OS_VERSION, os_version); |
| pseudo_hw_enforced->push_back(TAG_OS_PATCHLEVEL, os_patchlevel); |
| |
| // Honor caller creation, if provided. |
| if (!sw_enforced->Contains(TAG_CREATION_DATETIME)) { |
| sw_enforced->push_back(TAG_CREATION_DATETIME, java_time(time(nullptr))); |
| } |
| |
| return TranslateAuthorizationSetError(sw_enforced->is_valid()); |
| } |
| |
| keymaster_error_t UpgradeSoftKeyBlob(const UniquePtr<Key>& key, const uint32_t os_version, |
| const uint32_t os_patchlevel, |
| const AuthorizationSet& upgrade_params, |
| KeymasterKeyBlob* upgraded_key) { |
| bool set_changed = false; |
| |
| if (os_version == 0) { |
| // We need to allow "upgrading" OS version to zero, to support upgrading from proper |
| // numbered releases to unnumbered development and preview releases. |
| |
| int key_os_version_pos = key->sw_enforced().find(TAG_OS_VERSION); |
| if (key_os_version_pos != -1) { |
| uint32_t key_os_version = key->sw_enforced()[key_os_version_pos].integer; |
| if (key_os_version != 0) { |
| key->sw_enforced()[key_os_version_pos].integer = os_version; |
| set_changed = true; |
| } |
| } |
| } |
| |
| if (!UpgradeIntegerTag(TAG_OS_VERSION, os_version, &key->sw_enforced(), &set_changed) || |
| !UpgradeIntegerTag(TAG_OS_PATCHLEVEL, os_patchlevel, &key->sw_enforced(), &set_changed)) |
| // One of the version fields would have been a downgrade. Not allowed. |
| return KM_ERROR_INVALID_ARGUMENT; |
| |
| if (!set_changed) |
| // Dont' need an upgrade. |
| return KM_ERROR_OK; |
| |
| AuthorizationSet hidden; |
| auto error = BuildHiddenAuthorizations(upgrade_params, &hidden, softwareRootOfTrust); |
| if (error != KM_ERROR_OK) return error; |
| return SerializeIntegrityAssuredBlob(key->key_material(), hidden, key->hw_enforced(), |
| key->sw_enforced(), upgraded_key); |
| } |
| |
| } // namespace keymaster |