| /* |
| * Copyright (c) 2013-2021 TRUSTONIC LIMITED |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * 3. Neither the name of the TRUSTONIC LIMITED nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* Get PRIiN and friends from <inttypes.h> despite the C++ standard not |
| * wanting to let us. |
| */ |
| #define __STDC_FORMAT_MACROS |
| |
| #include <inttypes.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include "keymaster_ta_defs.h" |
| #include "tlcTeeKeymaster_if.h" |
| #include "km_util.h" |
| #include "serialization.h" |
| #include "authlist.h" |
| #include "km_encodings.h" |
| /* ExySp: add header for propery */ |
| #include "cutils/properties.h" |
| #include <unistd.h> |
| |
| #define RSA_MAX_KEY_SIZE 4096 |
| #define EC_MAX_KEY_SIZE 521 |
| #define AES_MAX_KEY_SIZE 256 |
| #define HMAC_MAX_KEY_SIZE 2048 |
| #define TRIPLE_DES_KEY_SIZE 192 /* Including parity bits */ |
| |
| /* HACK */ |
| #define KM_TAG_ATTESTATION_APPLICATION_ID (KM_BYTES | 709) |
| |
| #ifdef NDEBUG |
| /* In Release mode these macros are void. */ |
| #define PRINT_BUFFER(data, data_length) |
| #define PRINT_BLOB(blob) |
| #define PRINT_BLOB_HEX(blob, length) |
| #define PRINT_PARAM_SET(params) |
| |
| #else |
| #include <sstream> |
| |
| #define SBUF_SIZE 1024 |
| static char sbuf[SBUF_SIZE]; // for formatting debug output prior to LOG_I |
| |
| static int snprint_buffer( |
| char *s, size_t n, |
| const uint8_t *data, |
| size_t data_length) |
| { |
| /* ExySp */ |
| int wlen = 0, i, tmp_wlen; |
| |
| if (data == NULL) { |
| return snprintf(s, n, "<NULL>\n"); |
| } else { |
| wlen = snprintf(s, n, "<data of length %zu>\n", data_length); |
| s = s + wlen; |
| for (i = 0; i < (int)data_length; i++) { |
| tmp_wlen = snprintf(s, n - wlen, "0x%02x ", data[i]); |
| wlen += tmp_wlen; |
| s += tmp_wlen; |
| } |
| tmp_wlen = snprintf(s, n - wlen, "\n"); |
| wlen += tmp_wlen; |
| return wlen; |
| /* ExySp end */ |
| } |
| } |
| |
| static int snprint_uint64( |
| char *s, size_t n, |
| uint64_t x) |
| { |
| return snprintf(s, n, "0x%08x%08x\n", (uint32_t)(x >> 32), (uint32_t)(x & 0xFFFFFFFF)); |
| } |
| |
| static int snprint_bool( |
| char *s, size_t n, |
| bool x) |
| { |
| return snprintf(s, n, x ? "true\n" : "false\n"); |
| } |
| |
| static int snprint_blob( |
| char *s, size_t n, |
| const keymaster_blob_t *blob) |
| { |
| if (blob == NULL) { |
| return snprintf(s, n, "<NULL>\n"); |
| } else { |
| return snprint_buffer(s, n, blob->data, blob->data_length); |
| } |
| } |
| |
| static int snprint_param_set( |
| char *s, size_t n, |
| const keymaster_key_param_set_t *param_set) |
| { |
| if (param_set == NULL) { |
| return snprintf(s, n, "<NULL>\n"); |
| } |
| else { |
| int r; |
| size_t i = 0, l = 0; |
| while ((i < param_set->length) && (l < SBUF_SIZE)) { |
| const keymaster_key_param_t param = param_set->params[i]; |
| switch (param.tag) { |
| #define PRINT_ENUM snprintf(s + l, SBUF_SIZE - l, "0x%08x\n", param.enumerated) |
| #define PRINT_UINT snprintf(s + l, SBUF_SIZE - l, "%u\n", param.integer) |
| #define PRINT_ULONG snprint_uint64(s + l, SBUF_SIZE - l, param.long_integer) |
| #define PRINT_DATE snprint_uint64(s + l, SBUF_SIZE - l, param.date_time) |
| #define PRINT_BOOL snprint_bool(s + l, SBUF_SIZE - l, param.boolean) |
| // #define PRINT_BIGNUM |
| #define PRINT_BYTES snprint_blob(s + l, SBUF_SIZE - l, ¶m.blob) |
| #define PRINT_STR snprintf(s + l, SBUF_SIZE - l, "%s\n", param.blob.data) |
| #define PARAM_CASE(tag, printval) \ |
| case tag: \ |
| r = snprintf(s + l, SBUF_SIZE - l, "%s: ", #tag); \ |
| if (r < 0) return r; \ |
| l += r; \ |
| if (l < SBUF_SIZE) { \ |
| r = printval; \ |
| if (r < 0) return r; \ |
| l += r; \ |
| } \ |
| break; |
| #define PARAM_CASE_ENUM(tag) PARAM_CASE(tag, PRINT_ENUM) |
| #define PARAM_CASE_UINT(tag) PARAM_CASE(tag, PRINT_UINT) |
| #define PARAM_CASE_ULONG(tag) PARAM_CASE(tag, PRINT_ULONG) |
| #define PARAM_CASE_DATE(tag) PARAM_CASE(tag, PRINT_DATE) |
| #define PARAM_CASE_BOOL(tag) PARAM_CASE(tag, PRINT_BOOL) |
| // #define PARAM_CASE_BIGNUM |
| #define PARAM_CASE_BYTES(tag) PARAM_CASE(tag, PRINT_BYTES) |
| #define PARAM_CASE_STR(tag) PARAM_CASE(tag, PRINT_STR) |
| PARAM_CASE_DATE(KM_TAG_ACTIVE_DATETIME) |
| PARAM_CASE_ENUM(KM_TAG_ALGORITHM) |
| PARAM_CASE_BOOL(KM_TAG_ALL_APPLICATIONS) |
| PARAM_CASE_BOOL(KM_TAG_ALL_USERS) |
| PARAM_CASE_BOOL(KM_TAG_ALLOW_WHILE_ON_BODY) |
| PARAM_CASE_BYTES(KM_TAG_APPLICATION_DATA) |
| PARAM_CASE_BYTES(KM_TAG_APPLICATION_ID) |
| PARAM_CASE_BYTES(KM_TAG_ASSOCIATED_DATA) |
| PARAM_CASE_BYTES(KM_TAG_ATTESTATION_CHALLENGE) |
| PARAM_CASE_UINT(KM_TAG_AUTH_TIMEOUT) |
| PARAM_CASE_BYTES(KM_TAG_AUTH_TOKEN) |
| PARAM_CASE_ENUM(KM_TAG_BLOB_USAGE_REQUIREMENTS) |
| PARAM_CASE_ENUM(KM_TAG_BLOCK_MODE) |
| PARAM_CASE_UINT(KM_TAG_BOOT_PATCHLEVEL) |
| PARAM_CASE_BOOL(KM_TAG_BOOTLOADER_ONLY) |
| PARAM_CASE_BOOL(KM_TAG_CALLER_NONCE) |
| PARAM_CASE_BYTES(KM_TAG_CONFIRMATION_TOKEN) |
| PARAM_CASE_DATE(KM_TAG_CREATION_DATETIME) |
| PARAM_CASE_ENUM(KM_TAG_DIGEST) |
| PARAM_CASE_ENUM(KM_TAG_EC_CURVE) |
| PARAM_CASE_BOOL(KM_TAG_ECIES_SINGLE_HASH_MODE) |
| PARAM_CASE_BOOL(KM_TAG_EXPORTABLE) |
| PARAM_CASE_ENUM(KM_TAG_HARDWARE_TYPE) |
| PARAM_CASE_BOOL(KM_TAG_INCLUDE_UNIQUE_ID) |
| PARAM_CASE_ENUM(KM_TAG_KDF) |
| PARAM_CASE_UINT(KM_TAG_KEY_SIZE) |
| PARAM_CASE_UINT(KM_TAG_MAC_LENGTH) |
| PARAM_CASE_UINT(KM_TAG_MAX_USES_PER_BOOT) |
| PARAM_CASE_UINT(KM_TAG_MIN_MAC_LENGTH) |
| PARAM_CASE_UINT(KM_TAG_MIN_SECONDS_BETWEEN_OPS) |
| PARAM_CASE_BOOL(KM_TAG_NO_AUTH_REQUIRED) |
| PARAM_CASE_BYTES(KM_TAG_NONCE) |
| PARAM_CASE_ENUM(KM_TAG_ORIGIN) |
| PARAM_CASE_DATE(KM_TAG_ORIGINATION_EXPIRE_DATETIME) |
| PARAM_CASE_UINT(KM_TAG_OS_PATCHLEVEL) |
| PARAM_CASE_UINT(KM_TAG_OS_VERSION) |
| PARAM_CASE_ENUM(KM_TAG_PADDING) |
| PARAM_CASE_ENUM(KM_TAG_PURPOSE) |
| PARAM_CASE_BOOL(KM_TAG_RESET_SINCE_ID_ROTATION) |
| PARAM_CASE_BOOL(KM_TAG_ROLLBACK_RESISTANCE) |
| PARAM_CASE_BOOL(KM_TAG_ROLLBACK_RESISTANT) |
| PARAM_CASE_BYTES(KM_TAG_ROOT_OF_TRUST) |
| PARAM_CASE_ULONG(KM_TAG_RSA_PUBLIC_EXPONENT) |
| PARAM_CASE_BOOL(KM_TAG_TRUSTED_CONFIRMATION_REQUIRED) |
| PARAM_CASE_BOOL(KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED) |
| PARAM_CASE_BYTES(KM_TAG_UNIQUE_ID) |
| PARAM_CASE_BOOL(KM_TAG_UNLOCKED_DEVICE_REQUIRED) |
| PARAM_CASE_DATE(KM_TAG_USAGE_EXPIRE_DATETIME) |
| PARAM_CASE_ENUM(KM_TAG_USER_AUTH_TYPE) |
| PARAM_CASE_UINT(KM_TAG_USER_ID) |
| PARAM_CASE_ULONG(KM_TAG_USER_SECURE_ID) |
| PARAM_CASE_UINT(KM_TAG_VENDOR_PATCHLEVEL) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_BRAND) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_DEVICE) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_PRODUCT) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_SERIAL) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_IMEI) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_MEID) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_MANUFACTURER) |
| PARAM_CASE_STR(KM_TAG_ATTESTATION_ID_MODEL) |
| PARAM_CASE_BOOL(KM_TAG_EARLY_BOOT_ONLY) |
| PARAM_CASE_BOOL(KM_TAG_STORAGE_KEY) |
| default: |
| r = snprintf(s + l, SBUF_SIZE - l, "<unknown tag 0x%08x>\n", param.tag); |
| if (r < 0) return r; |
| l += r; |
| } |
| i++; |
| } |
| return l; |
| } |
| } |
| |
| #define PRINT_BUFFER(data, data_length) \ |
| do { \ |
| if (snprint_buffer(sbuf, SBUF_SIZE, data, data_length) >= 0) { \ |
| LOG_D("%s = %s", #data, sbuf); \ |
| } \ |
| } while (0) |
| #define PRINT_BLOB(blob) \ |
| do { \ |
| if (snprint_blob(sbuf, SBUF_SIZE, blob) >= 0) { \ |
| LOG_D("%s = %s", #blob, sbuf); \ |
| } \ |
| } while (0) |
| #define PRINT_PARAM_SET(params) \ |
| do { \ |
| if (snprint_param_set(sbuf, SBUF_SIZE, params) >= 0) { \ |
| LOG_D("%s =\n%s", #params, sbuf); \ |
| } \ |
| } while (0) |
| |
| #define PRINT_BLOB_HEX(blob, len) \ |
| do { \ |
| std::ostringstream buf; \ |
| for(size_t i=0; i<len; ++i) { \ |
| buf << std::hex << (unsigned) blob[i] << " "; \ |
| } \ |
| LOG_D("%s = %s\n", #blob, buf.str().c_str()); \ |
| } \ |
| while (0) |
| |
| #endif |
| |
| /* Global definitions */ |
| static const uint32_t gDeviceId = MC_DEVICE_ID_DEFAULT; |
| static const mcUuid_t gUuid = TEE_KEYMASTER_N_TA_UUID; |
| |
| extern inline void keymaster_free_param_values(keymaster_key_param_t* param, size_t param_count); |
| extern inline void keymaster_free_param_set(keymaster_key_param_set_t* set); |
| extern inline void keymaster_free_characteristics(keymaster_key_characteristics_t* characteristics); |
| |
| /* ExySp: add timeout to check setting property */ |
| #define SECURE_OS_TIMEOUT 50000 |
| |
| /* Acceptable params for all commands */ |
| #define AUTH_TAGS \ |
| KM_TAG_ALL_USERS, \ |
| KM_TAG_USER_ID, \ |
| KM_TAG_USER_SECURE_ID, \ |
| KM_TAG_NO_AUTH_REQUIRED, \ |
| KM_TAG_USER_AUTH_TYPE, \ |
| KM_TAG_AUTH_TIMEOUT, \ |
| KM_TAG_ALL_APPLICATIONS, \ |
| KM_TAG_APPLICATION_ID, \ |
| KM_TAG_APPLICATION_DATA |
| |
| /* Acceptable params for generate_key() and import_key() */ |
| #define KEY_CREATION_ALLOWED_TAGS \ |
| AUTH_TAGS, \ |
| KM_TAG_PURPOSE, \ |
| KM_TAG_ALGORITHM, \ |
| KM_TAG_KEY_SIZE, \ |
| KM_TAG_BLOCK_MODE, \ |
| KM_TAG_DIGEST, \ |
| KM_TAG_PADDING, \ |
| KM_TAG_CALLER_NONCE, \ |
| KM_TAG_MIN_MAC_LENGTH, \ |
| KM_TAG_EC_CURVE, \ |
| KM_TAG_RSA_PUBLIC_EXPONENT, \ |
| KM_TAG_ACTIVE_DATETIME, \ |
| KM_TAG_ORIGINATION_EXPIRE_DATETIME, \ |
| KM_TAG_USAGE_EXPIRE_DATETIME, \ |
| KM_TAG_MIN_SECONDS_BETWEEN_OPS, \ |
| KM_TAG_MAX_USES_PER_BOOT, \ |
| KM_TAG_INCLUDE_UNIQUE_ID, \ |
| KM_TAG_BOOTLOADER_ONLY, \ |
| KM_TAG_BLOB_USAGE_REQUIREMENTS, \ |
| KM_TAG_ALLOW_WHILE_ON_BODY, \ |
| KM_TAG_ROLLBACK_RESISTANCE, \ |
| KM_TAG_HARDWARE_TYPE, \ |
| KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED, \ |
| KM_TAG_TRUSTED_CONFIRMATION_REQUIRED, \ |
| KM_TAG_UNLOCKED_DEVICE_REQUIRED, \ |
| KM_TAG_EARLY_BOOT_ONLY, \ |
| KM_TAG_STORAGE_KEY |
| |
| static const keymaster_tag_t key_creation_allowed_params [] = { |
| KEY_CREATION_ALLOWED_TAGS |
| }; |
| |
| /* Acceptable params for import_key() */ |
| static const keymaster_tag_t key_import_allowed_params [] = { |
| KEY_CREATION_ALLOWED_TAGS, |
| KM_TAG_CREATION_DATETIME /* Allow this to work around Keystore bug. */ |
| }; |
| |
| /* Acceptable params for begin() */ |
| static const keymaster_tag_t op_begin_allowed_params [] = { |
| AUTH_TAGS, |
| KM_TAG_ALGORITHM, |
| KM_TAG_BLOCK_MODE, |
| KM_TAG_DIGEST, |
| KM_TAG_PADDING, |
| KM_TAG_ASSOCIATED_DATA, |
| KM_TAG_NONCE, |
| KM_TAG_AUTH_TOKEN, |
| KM_TAG_MAC_LENGTH, |
| KM_TAG_PURPOSE |
| }; |
| |
| /* Acceptable params for update() and finish() |
| * KM_TAG_DIGEST, KM_TAG_PADDING, KM_TAG_ALGORITHM are allowed, |
| * bit ignored in update() and finish() |
| * This is done to make VtsHalWifiKeystoreV1_0TargetTest pass. |
| */ |
| static const keymaster_tag_t op_allowed_params [] = { |
| AUTH_TAGS, |
| KM_TAG_ASSOCIATED_DATA, |
| KM_TAG_AUTH_TOKEN, |
| KM_TAG_CONFIRMATION_TOKEN, |
| KM_TAG_DIGEST, |
| KM_TAG_PADDING, |
| KM_TAG_ALGORITHM |
| }; |
| |
| /* Acceptable params for attest_key() */ |
| static const keymaster_tag_t attest_allowed_params [] = { |
| KM_TAG_ATTESTATION_CHALLENGE, |
| KM_TAG_RESET_SINCE_ID_ROTATION, |
| KM_TAG_APPLICATION_ID, |
| KM_TAG_APPLICATION_DATA, |
| KM_TAG_ATTESTATION_ID_BRAND, |
| KM_TAG_ATTESTATION_ID_DEVICE, |
| KM_TAG_ATTESTATION_ID_PRODUCT, |
| KM_TAG_ATTESTATION_ID_SERIAL, |
| KM_TAG_ATTESTATION_ID_IMEI, |
| KM_TAG_ATTESTATION_ID_MEID, |
| KM_TAG_ATTESTATION_ID_MANUFACTURER, |
| KM_TAG_ATTESTATION_ID_MODEL |
| }; |
| |
| /** |
| * Get value of enumerated tag from key parameters |
| */ |
| static keymaster_error_t get_enumerated_tag( |
| const keymaster_key_param_set_t *params, |
| keymaster_tag_t tag, |
| uint32_t *value) |
| { |
| if ( (params == NULL) || (value == NULL) ) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| for (size_t i = 0; i < params->length; i++) { |
| if (params->params[i].tag == tag) { |
| *value = params->params[i].enumerated; |
| return KM_ERROR_OK; |
| } |
| } |
| return KM_ERROR_INVALID_TAG; |
| } |
| |
| /** |
| * Get value of integer tag from key parameters |
| */ |
| static keymaster_error_t get_integer_tag( |
| const keymaster_key_param_set_t *params, |
| keymaster_tag_t tag, |
| uint32_t *value) |
| { |
| if ( (params == NULL) || (value == NULL) ) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| for (size_t i = 0; i < params->length; i++) { |
| if (params->params[i].tag == tag) { |
| *value = params->params[i].integer; |
| return KM_ERROR_OK; |
| } |
| } |
| return KM_ERROR_INVALID_TAG; |
| } |
| |
| /** |
| * Get value of long integer tag from key parameters |
| */ |
| static keymaster_error_t get_long_integer_tag( |
| const keymaster_key_param_set_t *params, |
| keymaster_tag_t tag, |
| uint64_t *value) |
| { |
| if ( (params == NULL) || (value == NULL) ) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| for (size_t i = 0; i < params->length; i++) { |
| if (params->params[i].tag == tag) { |
| *value = params->params[i].long_integer; |
| return KM_ERROR_OK; |
| } |
| } |
| return KM_ERROR_INVALID_TAG; |
| } |
| |
| /* ExySp: func to check if secureOS is loaded or not. */ |
| static int secure_os_init(void) |
| { |
| char state[PROPERTY_VALUE_MAX]; |
| int i; |
| |
| for (i = 0; i < SECURE_OS_TIMEOUT; i++) { |
| property_get("vendor.sys.mobicoredaemon.enable", state, 0); |
| if (!strncmp(state, "true", strlen("true") + 1)) |
| break; |
| else |
| usleep(500); |
| } |
| |
| if (i == SECURE_OS_TIMEOUT) { |
| LOG_E("%s: secure os init timed out!", __func__); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } else { |
| LOG_D("%s: secure os init is done", __func__); |
| return KM_ERROR_OK; |
| } |
| } |
| |
| /** |
| * Check that all tags in \p params occur in the given list. |
| */ |
| static keymaster_error_t check_params( |
| const keymaster_key_param_set_t *params, |
| const keymaster_tag_t *tags, |
| size_t tag_count) |
| { |
| if ((tags == NULL) && (tag_count != 0)) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| |
| if ((params != NULL) && (params->length != 0)) { |
| if (params->params == NULL) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| for (uint32_t i = 0; i < params->length; i++) { |
| keymaster_tag_t tag = params->params[i].tag; |
| bool ok = false; |
| size_t j = 0; |
| while (!ok && (j < tag_count)) { |
| ok = (tags[j] == tag); |
| j++; |
| } |
| if (!ok && (tag != KM_TAG_ATTESTATION_APPLICATION_ID)) { /* HACK */ |
| LOG_E("%s: Invalid tag 0x%08x", __func__, tag); |
| return KM_ERROR_INVALID_TAG; |
| } |
| } |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| /** |
| * Maximum supported key size in bits. |
| * |
| * @param algorithm key type |
| * @return maximum supported size in bits |
| */ |
| static uint32_t km_max_key_size( |
| keymaster_algorithm_t algorithm) |
| { |
| switch (algorithm) { |
| case KM_ALGORITHM_RSA: |
| return RSA_MAX_KEY_SIZE; |
| case KM_ALGORITHM_EC: |
| return EC_MAX_KEY_SIZE; |
| case KM_ALGORITHM_AES: |
| return AES_MAX_KEY_SIZE; |
| case KM_ALGORITHM_HMAC: |
| return HMAC_MAX_KEY_SIZE; |
| case KM_ALGORITHM_TRIPLE_DES: |
| return TRIPLE_DES_KEY_SIZE; |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * Upper bound km_key_data in plain form. |
| * |
| * @param algorithm key type |
| * @param key_size_in_bits key size in bits if known; if zero, assume worst case |
| * @return upper bound for km_key_data size |
| */ |
| static uint32_t km_key_data_max_size( |
| keymaster_algorithm_t algorithm, |
| uint32_t key_size_in_bits) |
| { |
| uint32_t keylen = BITS_TO_BYTES( |
| (key_size_in_bits > 0) ? key_size_in_bits : km_max_key_size(algorithm)); |
| uint32_t prelim_size = 4 + 4; // key type and key size, both uint32_t |
| switch(algorithm) |
| { |
| case KM_ALGORITHM_RSA: |
| /* n + e + d + p + q + dp + dq + qinv */ |
| return prelim_size + KM_RSA_METADATA_SIZE + 8 * keylen; |
| case KM_ALGORITHM_EC: |
| /* k + x + y */ |
| return prelim_size + KM_EC_METADATA_SIZE + 3 * keylen; |
| case KM_ALGORITHM_AES: |
| case KM_ALGORITHM_HMAC: |
| case KM_ALGORITHM_TRIPLE_DES: |
| /* no metadata */ |
| return prelim_size + keylen; |
| default: |
| /* Unsupported algorithm */ |
| return 0; |
| } |
| } |
| |
| /** |
| * Calculate upper bound on length of exported key data. |
| * |
| * @return upper bound on length of exported data, or 0 on error |
| */ |
| static uint32_t export_data_length( |
| keymaster_algorithm_t key_type, |
| uint32_t key_size) // bits |
| { |
| switch (key_type) { |
| case KM_ALGORITHM_AES: |
| /* ExySp hw_wrapped_key = aes 32 + gcm tag 16 + |
| aes 32 + secret 32 + secret_tag 16 */ |
| return BITS_TO_BYTES(key_size) + 32 + 32 + 16; |
| case KM_ALGORITHM_HMAC: |
| case KM_ALGORITHM_TRIPLE_DES: |
| // export not supported for symmetric keys |
| return 0; |
| case KM_ALGORITHM_RSA: |
| // type | size | size | n_len | e_len | n | e |
| return 5*4 + 2*BITS_TO_BYTES(key_size); |
| case KM_ALGORITHM_EC: |
| // type | size | curve | x_len | y_len | x | y |
| return 5*4 + 2*BITS_TO_BYTES(key_size); |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * Calculate upper bound for size in bytes of material in encrypted key blob. |
| * |
| * \param key_size_in_bits Key size in bits if known. If zero assume worst case. |
| */ |
| static uint32_t key_blob_max_size( |
| keymaster_algorithm_t algorithm, |
| uint32_t key_size_in_bits, |
| uint32_t params_size_in_bytes) |
| { |
| /* params_len (uint32_t) + caller-supplied params */ |
| uint32_t plain_material_size = 4 + params_size_in_bytes; |
| |
| /* characteristics set by us */ |
| plain_material_size += OWN_PARAMS_MAX_SIZE; |
| |
| /* raw key data */ |
| plain_material_size += km_key_data_max_size(algorithm, key_size_in_bits); |
| |
| /* |
| * Blob consists of: |
| * - 16-byte nonce, ciphertext, and 16-byte tag in version 0 |
| * - 4-byte header, 16-byte nonce, ciphertext, and 12-byte tag in version 1 |
| */ |
| return plain_material_size + 32; |
| } |
| |
| /** |
| * Map a buffer. |
| */ |
| keymaster_error_t map_buffer( |
| mcSessionHandle_t* session_handle, |
| const uint8_t *buf, uint32_t buflen, |
| mcBulkMap_t *bufinfo) |
| { |
| if ((buf != NULL) && (buflen != 0)) { |
| mcResult_t mcRet = mcMap(session_handle, (void*)buf, buflen, bufinfo); |
| if (mcRet != MC_DRV_OK) { |
| LOG_E("%s: mcMap() returned 0x%08x", __func__, mcRet); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } |
| } |
| return KM_ERROR_OK; |
| } |
| |
| /** |
| * Unmap a buffer. |
| */ |
| void unmap_buffer( |
| mcSessionHandle_t* session_handle, |
| const uint8_t *buf, |
| mcBulkMap_t *bufinfo) |
| { |
| if (bufinfo->sVirtualAddr != 0) { |
| mcResult_t mcRet = mcUnmap(session_handle, (void*)buf, bufinfo); |
| if (mcRet != MC_DRV_OK) { |
| LOG_E("%s: mcUnmap() returned 0x%08x", __func__, mcRet); |
| } |
| } |
| } |
| |
| /** |
| * Notify the trusted application and wait for response. |
| */ |
| keymaster_error_t transact( |
| mcSessionHandle_t* session_handle, |
| tciMessage_ptr tci) |
| { |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcResult_t mcRet; |
| |
| mcRet = mcNotify(session_handle); |
| if (mcRet != MC_DRV_OK) { |
| LOG_E("%s: mcNotify() returned 0x%08x", __func__, mcRet); |
| ret = KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| goto end; |
| } |
| |
| mcRet = mcWaitNotification(session_handle, MC_INFINITE_TIMEOUT); |
| if (mcRet != MC_DRV_OK) { |
| LOG_E("%s: mcWaitNotification() returned 0x%08x", __func__, mcRet); |
| ret = KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| goto end; |
| } |
| |
| CHECK_RESULT_OK( (keymaster_error_t)tci->response.header.returnCode ); |
| |
| end: |
| return ret; |
| } |
| |
| /** |
| * Find the operation record given the handle. |
| */ |
| static keymaster_error_t lookup_operation(TEE_Session *session, |
| keymaster_operation_handle_t handle, |
| struct operation **op) |
| { |
| size_t i; |
| keymaster_error_t ret = KM_ERROR_INVALID_OPERATION_HANDLE; |
| |
| for (i = 0; i < MAX_SESSION_NUM; i++) { |
| if (session->op[i].live && |
| session->op[i].handle == handle) { |
| *op = &session->op[i]; |
| ret = KM_ERROR_OK; |
| break; |
| } |
| } |
| if (ret) { |
| LOG_E("%s: op %#016" PRIx64 " not found", __func__, handle); |
| } |
| return ret; |
| } |
| |
| /** |
| * Find a spare operation slot. This can't fail; or, to put it another way, |
| * if there isn't a spare slot, we've done something wrong. |
| */ |
| static struct operation *find_spare_operation_slot(TEE_Session *session, |
| keymaster_operation_handle_t handle) |
| { |
| size_t i; |
| struct operation *op = NULL; |
| |
| for (i = 0; i < MAX_SESSION_NUM; i++) { |
| if (!session->op[i].live) { |
| op = &session->op[i]; |
| break; |
| } |
| } |
| assert(op); |
| op->live = true; |
| op->handle = handle; |
| session->live_ops++; |
| LOG_D("%s: allocate op %#016" PRIx64 " to slot #%zu (%u ops live)", |
| __func__, handle, i, session->live_ops); |
| return op; |
| } |
| |
| /** |
| * Release an operation slot. Do nothing if OP is null on entry. |
| */ |
| static void release_operation_slot(TEE_Session *session, |
| struct operation *op) |
| { |
| if (!op) return; |
| assert(op->live); |
| op->live = 0; |
| session->live_ops--; |
| LOG_D("%s: release op slot #%td (handle %#016" PRIx64 "; %u ops live)", |
| __func__, op - session->op, op->handle, session->live_ops); |
| } |
| |
| /** |
| * Data copy to memory with scope lifetime. |
| * |
| * If either buf or size is equal to NULL/0 function |
| * won't change state of data and it will return KM_ERROR_OK. |
| */ |
| static keymaster_error_t copy_to_scoped_buf(const uint8_t *buf, uint32_t size, scoped_buf_ptr_t& data) |
| { |
| if (buf+size < buf) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| if ( (buf!=NULL) && (size!=0) ) { |
| data.buf.reset(new (std::nothrow) uint8_t[size]); |
| if(data.buf) { |
| memcpy(data.buf.get(), buf, size); |
| data.size = size; |
| return KM_ERROR_OK; |
| } |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| return KM_ERROR_OK; |
| } |
| |
| int TEE_Open(TEE_SessionHandle *pSessionHandle) |
| { |
| struct TEE_Session *session; |
| mcResult_t mcRet; |
| int rc = 0; |
| size_t i; |
| |
| /* Validate session handle */ |
| if (pSessionHandle == NULL) { |
| LOG_E("%s: Invalid session handle", __func__); |
| return -EINVAL; |
| } |
| |
| /* ExySp: Check if secureOS is loaded or not */ |
| if (secure_os_init()) { |
| LOG_E("%s: Failed to init secureOS", __func__); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } |
| |
| session = (struct TEE_Session *)malloc(sizeof(*session)); |
| if (session == NULL) { |
| return -ENOMEM; |
| } |
| |
| /* Initialize session handle data */ |
| memset(session, 0, sizeof(*session)); |
| |
| /* Open MobiCore device */ |
| mcRet = mcOpenDevice(gDeviceId); |
| if ((MC_DRV_OK != mcRet) && |
| (MC_DRV_ERR_DEVICE_ALREADY_OPEN != mcRet)) |
| { |
| LOG_E("%s: mcOpenDevice() returned %d", __func__, mcRet); |
| rc = -EBUSY; |
| goto end_session; |
| } |
| |
| /* Allocating WSM for TCI */ |
| mcRet = mcMallocWsm(gDeviceId, 0, sizeof(*session->pTci), (uint8_t **) &session->pTci, 0); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcMallocWsm() returned %d", __func__, mcRet); |
| rc = -ENOMEM; |
| goto end_device; |
| } |
| |
| /* Open session the TEE Keymaster trusted application */ |
| session->sessionHandle.deviceId = gDeviceId; |
| mcRet = mcOpenSession(&session->sessionHandle, |
| &gUuid, |
| (uint8_t *) session->pTci, |
| (uint32_t) sizeof(tciMessage_t)); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcOpenSession() returned %d", __func__, mcRet); |
| mcFreeWsm(gDeviceId, (uint8_t*)session->pTci); |
| rc = -EBUSY; |
| goto end_device; |
| } |
| *pSessionHandle = (TEE_SessionHandle)session; |
| |
| /* No operations in progress yet. */ |
| for (i = 0; i < MAX_SESSION_NUM; i++) |
| session->op[i].live = false; |
| session->live_ops = 0; |
| goto end; |
| |
| end_device: |
| mcRet = mcCloseDevice(gDeviceId); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcCloseDevice() returned: %d", __func__, mcRet); |
| } |
| end_session: |
| free(session); |
| end: |
| return rc; |
| } |
| |
| void TEE_Close(TEE_SessionHandle sessionHandle) |
| { |
| mcResult_t mcRet; |
| |
| /* Validate session handle */ |
| if (sessionHandle == NULL) { |
| LOG_E("%s: Invalid session handle", __func__); |
| return; |
| } |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| |
| /* Close session */ |
| mcRet = mcCloseSession(&session->sessionHandle); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcCloseSession() returned %d", __func__, mcRet); |
| } |
| |
| mcFreeWsm(gDeviceId, (uint8_t*)session->pTci); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcFreeWsm() returned %d", __func__, mcRet); |
| } |
| |
| /* Close MobiCore device */ |
| mcRet = mcCloseDevice(gDeviceId); |
| if (MC_DRV_OK != mcRet) { |
| LOG_E("%s: mcCloseDevice() returned %d", __func__, mcRet); |
| } |
| free(session); |
| } |
| |
| keymaster_error_t TEE_Configure( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_param_set_t *params) |
| { |
| LOG_D("TEE_Configure"); |
| PRINT_PARAM_SET(params); |
| |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcBulkMap_t param_map = {0, 0}; |
| scoped_buf_ptr_t serialized_params; |
| |
| CHECK_RESULT_OK(km_serialize_params(serialized_params, |
| params, false, 0, 0)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| serialized_params.buf.get(), serialized_params.size, ¶m_map)); |
| |
| tci->command.header.commandId = CMD_ID_TEE_CONFIGURE; |
| tci->configure.params.data = (uint32_t)param_map.sVirtualAddr; |
| tci->configure.params.data_length = param_map.sVirtualLen; |
| |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| end: |
| unmap_buffer(session_handle, serialized_params.buf.get(), ¶m_map); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_AddRngEntropy( |
| TEE_SessionHandle sessionHandle, |
| const uint8_t *data, |
| uint32_t dataLength) |
| { |
| LOG_D("TEE_AddRngEntropy"); |
| PRINT_BUFFER(data, dataLength); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcBulkMap_t dataInfo = {0, 0}; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| scoped_buf_ptr_t data1; |
| |
| if (dataLength == 0) { |
| return KM_ERROR_OK; |
| } |
| CHECK_TRUE(KM_ERROR_INVALID_INPUT_LENGTH, |
| dataLength <= 16384); |
| |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK(copy_to_scoped_buf(data, dataLength, data1)); |
| |
| /* Map data */ |
| CHECK_RESULT_OK( map_buffer(session_handle, data1.buf.get(), data1.size, &dataInfo) ); |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_ADD_RNG_ENTROPY; |
| tci->add_rng_entropy.rng_data.data = (uint32_t)dataInfo.sVirtualAddr; |
| tci->add_rng_entropy.rng_data.data_length = dataInfo.sVirtualLen; |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| end: |
| unmap_buffer(session_handle, data1.buf.get(), &dataInfo); |
| if (data1.buf) { |
| memset(data1.buf.get(), 0, data1.size); |
| } |
| |
| LOG_D("TEE_AddRngEntropy exiting with %d", ret); |
| return ret; |
| } |
| |
| /** |
| * Check if we can create a key of a given size |
| * @param algorithm key type |
| * @param keysize key size in bits as expressed by KM_TAG_KEY_SIZE |
| * @return whether key size is supported |
| */ |
| static bool key_size_supported( |
| keymaster_algorithm_t algorithm, |
| size_t keysize) |
| { |
| switch (algorithm) { |
| case KM_ALGORITHM_RSA: |
| return ((keysize >= 256) && |
| (keysize <= RSA_MAX_KEY_SIZE) && |
| (keysize % 64 == 0)); |
| case KM_ALGORITHM_EC: |
| return ((keysize == 192) || |
| (keysize == 224) || |
| (keysize == 256) || |
| (keysize == 384) || |
| (keysize == 521)); |
| case KM_ALGORITHM_AES: |
| return ((keysize == 128) || |
| (keysize == 192) || |
| (keysize == 256)); |
| case KM_ALGORITHM_HMAC: |
| return ((keysize >= 64) && |
| (keysize <= HMAC_MAX_KEY_SIZE) && |
| (keysize % 8 == 0)); |
| case KM_ALGORITHM_TRIPLE_DES: |
| return (keysize == 168); |
| default: |
| return false; |
| } |
| } |
| |
| keymaster_error_t TEE_GenerateKey( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_param_set_t* params, |
| keymaster_key_blob_t* key_blob, |
| keymaster_key_characteristics_t* characteristics) |
| { |
| LOG_D("TEE_GenerateKey"); |
| PRINT_PARAM_SET(params); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcBulkMap_t paramsInfo = {0, 0}; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t characteristicsInfo = {0, 0}; |
| uint32_t encoded_key_size = 0; |
| uint32_t keySizeInBits = 0; |
| keymaster_ec_curve_t curve; |
| uint64_t rsa_pubexp = 0; |
| keymaster_algorithm_t algorithm; |
| scoped_buf_ptr_t serializedData; |
| uint8_t *key_chars = NULL; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| size_t max_charsz = 0; |
| |
| if (characteristics) { |
| characteristics->hw_enforced = { NULL, 0 }; |
| characteristics->sw_enforced = { NULL, 0 }; |
| } |
| CHECK_NOT_NULL(params); |
| CHECK_NOT_NULL(key_blob); |
| |
| key_blob->key_material = NULL; |
| |
| /* Check parameters are valid for generate_key */ |
| CHECK_RESULT_OK(check_params(params, key_creation_allowed_params, |
| sizeof(key_creation_allowed_params) / sizeof(keymaster_tag_t)) ); |
| |
| /* Find algorithm, key size and RSA public exponent */ |
| CHECK_RESULT_OK( get_enumerated_tag(params, |
| KM_TAG_ALGORITHM, reinterpret_cast<uint32_t*>(&algorithm))); |
| if (KM_ERROR_OK != get_integer_tag(params, KM_TAG_KEY_SIZE, &keySizeInBits)) { |
| if ((algorithm == KM_ALGORITHM_EC) && |
| (KM_ERROR_OK == get_enumerated_tag(params, |
| KM_TAG_EC_CURVE, reinterpret_cast<uint32_t*>(&curve)))) |
| { |
| keySizeInBits = ec_bitlen(curve); |
| } else { |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_SIZE, !"Unable to determine key size"); |
| } |
| } |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_SIZE, |
| key_size_supported(algorithm, keySizeInBits)); |
| if (algorithm == KM_ALGORITHM_RSA) { |
| if (KM_ERROR_OK == get_long_integer_tag(params, |
| KM_TAG_RSA_PUBLIC_EXPONENT, &rsa_pubexp)) |
| { |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| (rsa_pubexp % 2 == 1) && (rsa_pubexp != 1)); |
| } else { |
| rsa_pubexp = 65537; // default |
| } |
| } |
| |
| /* Serialize key parameters */ |
| CHECK_RESULT_OK(km_serialize_params( |
| serializedData, params, true, 0, rsa_pubexp)); |
| |
| /* Map key generation parameters */ |
| CHECK_RESULT_OK( map_buffer(session_handle, serializedData.buf.get(), serializedData.size, ¶msInfo) ); |
| |
| /* Allocate memory for key material */ |
| if (algorithm == KM_ALGORITHM_TRIPLE_DES) { |
| CHECK_TRUE(KM_ERROR_UNKNOWN_ERROR, keySizeInBits == 168); |
| encoded_key_size = 192; |
| } else { |
| encoded_key_size = keySizeInBits; |
| } |
| key_blob->key_material_size = key_blob_max_size( |
| algorithm, encoded_key_size, serializedData.size); |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&key_blob->key_material, key_blob->key_material_size)); |
| |
| /* Map key blob buffer */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_blob->key_material, key_blob->key_material_size, &keyBlobInfo) ); |
| |
| if (characteristics != NULL) { |
| /* Allocate memory for the key characteristics. */ |
| max_charsz = serializedData.size + OWN_PARAMS_MAX_SIZE; |
| CHECK_RESULT_OK(km_alloc(&key_chars, max_charsz)); |
| /* Map buffer for key characteristics. */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_chars, max_charsz, &characteristicsInfo) ); |
| } |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_GENERATE_KEY; |
| tci->generate_key.params.data = (uint32_t)paramsInfo.sVirtualAddr; |
| tci->generate_key.params.data_length = serializedData.size; |
| tci->generate_key.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->generate_key.key_blob.data_length = key_blob->key_material_size; |
| tci->generate_key.characteristics.data = (uint32_t)characteristicsInfo.sVirtualAddr; |
| tci->generate_key.characteristics.data_length = max_charsz; |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| /* Update key blob length */ |
| key_blob->key_material_size = tci->generate_key.key_blob.data_length; |
| |
| if (characteristics != NULL) { // Give characteristics to caller. |
| CHECK_RESULT_OK(km_deserialize_characteristics( |
| characteristics, key_chars, max_charsz)); |
| } |
| end: |
| unmap_buffer(session_handle, serializedData.buf.get(), ¶msInfo); |
| if (key_blob != NULL) { |
| unmap_buffer(session_handle, key_blob->key_material, &keyBlobInfo); |
| } |
| if (characteristics != NULL) { |
| unmap_buffer(session_handle, key_chars, &characteristicsInfo); |
| } |
| |
| free(key_chars); |
| |
| if (ret != KM_ERROR_OK) { |
| if (key_blob != NULL) { |
| free((void*)key_blob->key_material); |
| key_blob->key_material = NULL; |
| key_blob->key_material_size = 0; |
| } |
| if (characteristics != NULL) { |
| keymaster_free_characteristics(characteristics); |
| } |
| } |
| |
| LOG_D("TEE_GenerateKey exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_GetKeyCharacteristics( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_blob_t* key_blob, |
| const keymaster_blob_t* client_id, |
| const keymaster_blob_t* app_data, |
| keymaster_key_characteristics_t* characteristics) |
| { |
| LOG_D("TEE_GetKeyCharacteristics"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| uint8_t *key_chars = NULL; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t clientIdInfo = {0, 0}; |
| mcBulkMap_t appDataInfo = {0, 0}; |
| mcBulkMap_t characteristicsInfo = {0, 0}; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| scoped_buf_ptr_t key_blob1; |
| scoped_buf_ptr_t client_id1; |
| scoped_buf_ptr_t app_data1; |
| size_t max_charsz; |
| |
| CHECK_NOT_NULL(tci); |
| CHECK_NOT_NULL(key_blob); |
| CHECK_NOT_NULL(characteristics); |
| |
| /* Map input buffers */ |
| if ( key_blob != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(key_blob->key_material, key_blob->key_material_size, key_blob1) ); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_blob1.buf.get(), key_blob->key_material_size, &keyBlobInfo) ); |
| } |
| |
| if ( client_id != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(client_id->data, client_id->data_length, client_id1)); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| client_id1.buf.get(), client_id1.size, &clientIdInfo) ); |
| } |
| if ( app_data != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(app_data->data, app_data->data_length, app_data1)); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| app_data1.buf.get(), app_data1.size, &appDataInfo) ); |
| } |
| |
| /* Allocate memory for characteristics */ |
| max_charsz = key_blob->key_material_size; |
| CHECK_RESULT_OK(km_alloc(&key_chars, max_charsz)); |
| |
| /* Map buffer for serialized key characteristics */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_chars, max_charsz, &characteristicsInfo) ); |
| |
| /* Now the get_key_characteristics command */ |
| tci->command.header.commandId = CMD_ID_TEE_GET_KEY_CHARACTERISTICS; |
| tci->get_key_characteristics.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->get_key_characteristics.key_blob.data_length = key_blob->key_material_size; |
| tci->get_key_characteristics.client_id.data = (uint32_t)clientIdInfo.sVirtualAddr; |
| tci->get_key_characteristics.client_id.data_length = clientIdInfo.sVirtualLen; |
| tci->get_key_characteristics.app_data.data = (uint32_t)appDataInfo.sVirtualAddr; |
| tci->get_key_characteristics.app_data.data_length = appDataInfo.sVirtualLen; |
| tci->get_key_characteristics.characteristics.data = (uint32_t)characteristicsInfo.sVirtualAddr; |
| tci->get_key_characteristics.characteristics.data_length = max_charsz; |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| /* Deserialize */ |
| CHECK_RESULT_OK(km_deserialize_characteristics( |
| characteristics, key_chars, max_charsz)); |
| |
| end: |
| if (key_blob1.buf) { |
| unmap_buffer(session_handle, key_blob1.buf.get(), &keyBlobInfo); |
| } |
| if (client_id1.buf) { |
| unmap_buffer(session_handle, client_id1.buf.get(), &clientIdInfo); |
| } |
| if (app_data1.buf) { |
| unmap_buffer(session_handle, app_data1.buf.get(), &appDataInfo); |
| } |
| |
| if (key_chars != NULL) { |
| unmap_buffer(session_handle, key_chars, &characteristicsInfo); |
| } |
| |
| free(key_chars); |
| |
| if (ret != KM_ERROR_OK) { |
| if (characteristics != NULL) { |
| keymaster_free_characteristics(characteristics); |
| } |
| } |
| |
| LOG_D("TEE_GetKeyCharacteristics exiting with %d", ret); |
| return ret; |
| } |
| |
| /** |
| * Allocate and populate a buffer with encoded key data. |
| * |
| * @param format desired export format |
| * @param key_type key type |
| * @param key_size key size in bits |
| * @param core_pub_data public key data |
| * @param core_pub_data_len length of \p core_pub_data |
| * @param[out] export_data encoded key data |
| * |
| * @return KM_ERROR_OK or error |
| */ |
| static keymaster_error_t encode_key( |
| keymaster_key_format_t format, |
| keymaster_algorithm_t key_type, |
| uint32_t key_size, |
| const uint8_t *core_pub_data, |
| uint32_t core_pub_data_len, |
| keymaster_blob_t *export_data) |
| { |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| assert(export_data != NULL); |
| |
| export_data->data = NULL; |
| export_data->data_length = 0; |
| |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_FORMAT, |
| format == KM_KEY_FORMAT_X509); |
| |
| CHECK_RESULT_OK(encode_x509(export_data, key_type, key_size, |
| core_pub_data, core_pub_data_len)); |
| // no need to free export_data on error |
| |
| end: |
| return ret; |
| } |
| |
| keymaster_error_t TEE_ImportKey( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_param_set_t* params, |
| keymaster_key_format_t key_format, |
| const keymaster_blob_t* key_data, // encoded |
| keymaster_key_blob_t* key_blob, |
| keymaster_key_characteristics_t* characteristics) |
| { |
| LOG_D("TEE_ImportKey"); |
| PRINT_PARAM_SET(params); |
| LOG_D("key_format = 0x%08x", key_format); |
| PRINT_BLOB(key_data); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| scoped_buf_ptr_t serializedData; |
| mcBulkMap_t paramsInfo = {0, 0}; |
| mcBulkMap_t keyDataInfo = {0, 0}; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t characteristicsInfo = {0, 0}; |
| uint32_t keySizeInBits = 0; |
| uint64_t rsaPubExp = 0; |
| uint32_t decoded_key_size = 0; |
| uint32_t decoded_key_size_nominal = 0; |
| uint64_t rsa_e = 0; |
| keymaster_algorithm_t algorithm; |
| keymaster_ec_curve_t curve; |
| uint8_t *key_chars = NULL; |
| uint8_t *km_key_data = NULL; // to hold km_key_data passed to TA |
| size_t km_key_data_len = 0; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| size_t max_charsz = 0; |
| |
| CHECK_NOT_NULL(tci); |
| CHECK_NOT_NULL(key_blob); |
| |
| key_blob->key_material = NULL; |
| |
| /* Check parameters are valid for import_key */ |
| CHECK_RESULT_OK(check_params(params, key_import_allowed_params, |
| sizeof(key_import_allowed_params) / sizeof(keymaster_tag_t)) ); |
| |
| /* Extract algorithm and (if present) key size and RSA public exponent from |
| * the key parameters */ |
| CHECK_RESULT_OK( get_enumerated_tag(params, KM_TAG_ALGORITHM, |
| reinterpret_cast<uint32_t*>(&algorithm)) ); |
| if (KM_ERROR_OK != get_integer_tag(params, KM_TAG_KEY_SIZE, &keySizeInBits)) { |
| if (algorithm == KM_ALGORITHM_EC) { |
| if (KM_ERROR_OK == get_enumerated_tag(params, |
| KM_TAG_EC_CURVE, reinterpret_cast<uint32_t*>(&curve))) |
| { |
| keySizeInBits = ec_bitlen(curve); |
| } |
| } |
| } |
| CHECK_RESULT_OK(decode_key_data(&km_key_data, &km_key_data_len, |
| key_format, algorithm, key_data->data, key_data->data_length)); |
| |
| CHECK_TRUE(KM_ERROR_UNKNOWN_ERROR, km_key_data_len >= 8); |
| decoded_key_size = get_u32(km_key_data + 4); |
| if (algorithm == KM_ALGORITHM_TRIPLE_DES) { |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_SIZE, decoded_key_size == 192); |
| decoded_key_size_nominal = 168; |
| } else { |
| decoded_key_size_nominal = decoded_key_size; |
| } |
| if (keySizeInBits == 0) { |
| keySizeInBits = decoded_key_size_nominal; |
| } else { |
| CHECK_TRUE(KM_ERROR_IMPORT_PARAMETER_MISMATCH, |
| keySizeInBits == decoded_key_size_nominal); |
| } |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_SIZE, |
| key_size_supported(algorithm, keySizeInBits)); |
| |
| if (algorithm == KM_ALGORITHM_RSA) { |
| CHECK_TRUE(KM_ERROR_UNKNOWN_ERROR, km_key_data_len >= 20); |
| uint32_t n_len = get_u32(km_key_data + 12); |
| uint32_t e_len = get_u32(km_key_data + 16); |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, e_len <= 8); |
| CHECK_TRUE(KM_ERROR_UNKNOWN_ERROR, km_key_data_len >= 44 + n_len + e_len); |
| for (uint8_t i = 0; i < e_len; i++) { |
| rsa_e += (uint64_t)km_key_data[44 + n_len + i] << (8*(e_len - 1 - i)); |
| } |
| if (KM_ERROR_OK == get_long_integer_tag(params, KM_TAG_RSA_PUBLIC_EXPONENT, &rsaPubExp)) { |
| CHECK_TRUE(KM_ERROR_IMPORT_PARAMETER_MISMATCH, |
| rsa_e == rsaPubExp); |
| } |
| } |
| |
| /* Serialize key parameters */ |
| CHECK_RESULT_OK(km_serialize_params( |
| serializedData, params, true, keySizeInBits, rsa_e)); |
| |
| /* Map key parameters */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| serializedData.buf.get(), serializedData.size, ¶msInfo) ); |
| |
| /* Allocate memory for key blob */ |
| key_blob->key_material_size = key_blob_max_size( |
| algorithm, decoded_key_size, serializedData.size); |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&key_blob->key_material, key_blob->key_material_size)); |
| |
| /* Map key data */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| km_key_data, km_key_data_len, &keyDataInfo) ); |
| |
| /* Map key blob buffer */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_blob->key_material, key_blob->key_material_size, &keyBlobInfo) ); |
| |
| if (characteristics != NULL) { |
| max_charsz = serializedData.size + OWN_PARAMS_MAX_SIZE; |
| /* Allocate memory for the serialized key characteristics.*/ |
| CHECK_RESULT_OK(km_alloc(&key_chars, max_charsz)); |
| /* Map buffer for key characteristics. */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_chars, max_charsz, &characteristicsInfo) ); |
| } |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_IMPORT_KEY; |
| tci->import_key.params.data = (uint32_t)paramsInfo.sVirtualAddr; |
| tci->import_key.params.data_length = serializedData.size; |
| tci->import_key.key_data.data = (uint32_t)keyDataInfo.sVirtualAddr; |
| tci->import_key.key_data.data_length = km_key_data_len; |
| tci->import_key.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->import_key.key_blob.data_length = key_blob->key_material_size; |
| tci->import_key.characteristics.data = (uint32_t)characteristicsInfo.sVirtualAddr; |
| tci->import_key.characteristics.data_length = max_charsz; |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| /* Update key blob length */ |
| key_blob->key_material_size = tci->import_key.key_blob.data_length; |
| |
| if (characteristics != NULL) { // Give characteristics to caller. |
| CHECK_RESULT_OK(km_deserialize_characteristics( |
| characteristics, key_chars, max_charsz)); |
| } |
| |
| end: |
| unmap_buffer(session_handle, serializedData.buf.get(), ¶msInfo); |
| unmap_buffer(session_handle, km_key_data, &keyDataInfo); |
| if (key_blob != NULL) { |
| unmap_buffer(session_handle, (uint8_t*)key_blob->key_material, &keyBlobInfo); |
| } |
| if (key_chars != NULL) { |
| unmap_buffer(session_handle, key_chars, &characteristicsInfo); |
| } |
| |
| free(km_key_data); |
| free(key_chars); |
| |
| if (ret != KM_ERROR_OK) { |
| if (key_blob != NULL) { |
| free((void*)key_blob->key_material); |
| key_blob->key_material = NULL; |
| key_blob->key_material_size = 0; |
| } |
| if (characteristics != NULL) { |
| keymaster_free_characteristics(characteristics); |
| } |
| } |
| |
| LOG_D("TEE_ImportKey exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_ExportKey( |
| TEE_SessionHandle sessionHandle, |
| keymaster_key_format_t export_format, |
| const keymaster_key_blob_t* key_to_export, |
| const keymaster_blob_t* client_id, |
| const keymaster_blob_t* app_data, |
| keymaster_blob_t* export_data) |
| { |
| LOG_D("TEE_ExportKey"); |
| LOG_D("export_format = 0x%08x", export_format); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t clientIdInfo = {0, 0}; |
| mcBulkMap_t appDataInfo = {0, 0}; |
| mcBulkMap_t keyDataInfo = {0, 0}; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| uint8_t *core_pub_data = NULL; |
| uint32_t core_pub_data_len = 0; |
| keymaster_algorithm_t key_type; |
| uint32_t key_size; |
| scoped_buf_ptr_t key_to_export1; |
| scoped_buf_ptr_t client_id1; |
| scoped_buf_ptr_t app_data1; |
| |
| CHECK_NOT_NULL(tci); |
| CHECK_NOT_NULL(key_to_export); |
| CHECK_NOT_NULL(export_data); |
| |
| export_data->data = NULL; |
| |
| /* Map input buffers */ |
| if ( key_to_export != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(key_to_export->key_material, key_to_export->key_material_size, key_to_export1) ); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_to_export1.buf.get(), key_to_export->key_material_size, &keyBlobInfo) ); |
| } |
| |
| if ( client_id != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(client_id->data, client_id->data_length, client_id1)); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| client_id1.buf.get(), client_id1.size, &clientIdInfo) ); |
| } |
| if ( app_data != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(app_data->data, app_data->data_length, app_data1)); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| app_data1.buf.get(), app_data1.size, &appDataInfo) ); |
| } |
| |
| /* First need to determine required length of key data. */ |
| tci->command.header.commandId = CMD_ID_TEE_GET_KEY_INFO; |
| tci->get_key_info.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->get_key_info.key_blob.data_length = key_to_export->key_material_size; |
| tci->get_key_info.client_id.data = (uint32_t)clientIdInfo.sVirtualAddr; |
| tci->get_key_info.client_id.data_length = clientIdInfo.sVirtualLen; |
| tci->get_key_info.app_data.data = (uint32_t)appDataInfo.sVirtualAddr; |
| tci->get_key_info.app_data.data_length = appDataInfo.sVirtualLen; |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| key_type = (keymaster_algorithm_t)tci->get_key_info.key_type; |
| key_size = tci->get_key_info.key_size; |
| |
| core_pub_data_len = export_data_length(key_type, key_size); |
| CHECK_TRUE(KM_ERROR_UNSUPPORTED_KEY_FORMAT, |
| core_pub_data_len > 0); |
| |
| /* Allocate memory for exported public key data */ |
| CHECK_RESULT_OK(km_alloc(&core_pub_data, core_pub_data_len)); |
| |
| /* Map buffer */ |
| CHECK_RESULT_OK( map_buffer(session_handle, core_pub_data, core_pub_data_len, &keyDataInfo) ); |
| |
| /* Now the export_key command */ |
| tci->command.header.commandId = CMD_ID_TEE_EXPORT_KEY; |
| tci->export_key.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->export_key.key_blob.data_length = key_to_export->key_material_size; |
| tci->export_key.client_id.data = (uint32_t)clientIdInfo.sVirtualAddr; |
| tci->export_key.client_id.data_length = clientIdInfo.sVirtualLen; |
| tci->export_key.app_data.data = (uint32_t)appDataInfo.sVirtualAddr; |
| tci->export_key.app_data.data_length = appDataInfo.sVirtualLen; |
| tci->export_key.key_data.data = (uint32_t)keyDataInfo.sVirtualAddr; |
| tci->export_key.key_data.data_length = core_pub_data_len; |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| /* ExySp */ |
| if (key_type == KM_ALGORITHM_RSA || key_type == KM_ALGORITHM_EC) { |
| /* Allocate and fill buffer for encoded key data for passing to caller */ |
| CHECK_RESULT_OK(encode_key(export_format, key_type, key_size, |
| core_pub_data, core_pub_data_len, export_data)); |
| } |
| else if (key_type == KM_ALGORITHM_AES) { |
| export_data->data = (uint8_t*)malloc(core_pub_data_len); |
| memcpy((uint8_t *)export_data->data, core_pub_data, core_pub_data_len); |
| export_data->data_length = core_pub_data_len; |
| } |
| end: |
| if (key_to_export1.buf) |
| { |
| unmap_buffer(session_handle, key_to_export1.buf.get(), &keyBlobInfo); |
| } |
| if (client_id1.buf) { |
| unmap_buffer(session_handle, client_id1.buf.get(), &clientIdInfo); |
| } |
| if (app_data1.buf) { |
| unmap_buffer(session_handle, app_data1.buf.get(), &appDataInfo); |
| } |
| unmap_buffer(session_handle, (uint8_t*)core_pub_data, &keyDataInfo); |
| |
| if (ret != KM_ERROR_OK) { |
| if (export_data != NULL) { |
| free((void*)export_data->data); |
| export_data->data = NULL; |
| export_data->data_length = 0; |
| } |
| } |
| |
| free(core_pub_data); |
| |
| LOG_D("TEE_ExportKey exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_AttestKey( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_blob_t* key_to_attest, |
| const keymaster_key_param_set_t* attest_params, |
| keymaster_cert_chain_t* cert_chain) |
| { |
| LOG_D("TEE_AttestKey"); |
| PRINT_PARAM_SET(attest_params); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| struct TEE_Session *session = (struct TEE_Session*)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t paramsInfo = {0, 0}; |
| mcBulkMap_t certsInfo = {0, 0}; |
| scoped_buf_ptr_t serializedParams; |
| scoped_buf_ptr_t key_to_attest1; |
| uint8_t *certs = NULL; |
| |
| CHECK_NOT_NULL(key_to_attest); |
| CHECK_NOT_NULL(cert_chain); |
| |
| /* Check parameters are valid for attest_key */ |
| CHECK_RESULT_OK(check_params(attest_params, attest_allowed_params, |
| sizeof(attest_allowed_params) / sizeof(keymaster_tag_t))); |
| |
| /* Map input const buffer key_to_attest */ |
| if ( key_to_attest != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(key_to_attest->key_material, key_to_attest->key_material_size, key_to_attest1) ); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_to_attest1.buf.get(), key_to_attest->key_material_size, &keyBlobInfo) ); |
| } |
| |
| /* Serialize attestation parameters */ |
| CHECK_RESULT_OK(km_serialize_params( |
| serializedParams, attest_params, false, 0, 0)); |
| |
| /* Map attestation parameters */ |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| serializedParams.buf.get(), serializedParams.size, ¶msInfo)); |
| |
| /* Allocate memory for the serialized cert chain */ |
| CHECK_RESULT_OK(km_alloc(&certs, KM_CERT_CHAIN_SIZE)); |
| |
| /* Map buffer for serialized cert chain */ |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| certs, KM_CERT_CHAIN_SIZE, &certsInfo)); |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_ATTEST_KEY; |
| tci->attest_key.key_to_attest.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->attest_key.key_to_attest.data_length = keyBlobInfo.sVirtualLen; |
| tci->attest_key.attest_params.data = (uint32_t)paramsInfo.sVirtualAddr; |
| tci->attest_key.attest_params.data_length = paramsInfo.sVirtualLen; |
| tci->attest_key.cert_chain.data = (uint32_t)certsInfo.sVirtualAddr; |
| tci->attest_key.cert_chain.data_length = certsInfo.sVirtualLen; |
| |
| /* Talk to TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* Deserialize certs -> cert_chain, allocating memory for the latter. */ |
| CHECK_RESULT_OK(km_deserialize_attestation(cert_chain, certs, KM_CERT_CHAIN_SIZE)); |
| |
| end: |
| if (key_to_attest1.buf) { |
| unmap_buffer(session_handle, |
| key_to_attest1.buf.get(), &keyBlobInfo); |
| } |
| if (serializedParams.buf) { |
| unmap_buffer(session_handle, serializedParams.buf.get(), ¶msInfo); |
| } |
| if (certs != NULL) { |
| unmap_buffer(session_handle, certs, &certsInfo); |
| } |
| free(certs); |
| |
| if (ret != KM_ERROR_OK) { |
| keymaster_free_cert_chain(cert_chain); |
| } |
| |
| LOG_D("TEE_AttestKey exiting with %d", ret); |
| return ret; |
| } |
| |
| #define UPGRADE_OVERHEAD (4*(4 + 4)) |
| /* Space for the additional parameters which `upgrade_key' might insert into the |
| * blob. Four parameters, each with 32-bit tag and value. |
| */ |
| |
| keymaster_error_t TEE_UpgradeKey( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_key_blob_t* key_to_upgrade, |
| const keymaster_key_param_set_t* upgrade_params, |
| keymaster_key_blob_t* upgraded_key) |
| { |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcBulkMap_t keyin_map = { 0, 0 }, keyout_map = { 0, 0 }; |
| mcBulkMap_t param_map = { 0, 0 }; |
| scoped_buf_ptr_t serialized_params; |
| scoped_buf_ptr_t key_to_upgrade1; |
| uint8_t *kbuf = NULL; |
| size_t kbufsz; |
| size_t newblobsz = 0; |
| |
| /* Picky checking on input pointers. */ |
| CHECK_NOT_NULL(key_to_upgrade); |
| CHECK_NOT_NULL(upgrade_params); |
| CHECK_NOT_NULL(upgraded_key); |
| |
| /* Map input const buffer key_to_upgrade */ |
| if ( key_to_upgrade != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(key_to_upgrade->key_material, key_to_upgrade->key_material_size, key_to_upgrade1) ); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_to_upgrade1.buf.get(), key_to_upgrade->key_material_size, &keyin_map) ); |
| } |
| |
| CHECK_RESULT_OK(km_serialize_params(serialized_params, |
| upgrade_params, false, 0, 0)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| serialized_params.buf.get(), serialized_params.size, ¶m_map)); |
| |
| /* Create and map output buffer -- which means we have to guess how big to |
| * make it. If this is a really old key, it won't have the version |
| * parameters at all, so it'll come out UPGRADE_OVERHEAD bytes bigger than |
| * the one we fed in. Otherwise, it should be the same length. Of course, |
| * we can't tell which from here... |
| */ |
| kbufsz = key_to_upgrade->key_material_size + UPGRADE_OVERHEAD; |
| CHECK_RESULT_OK(km_alloc(&kbuf, kbufsz)); |
| CHECK_RESULT_OK(map_buffer(session_handle, kbuf, kbufsz, &keyout_map)); |
| |
| /* Build the command. */ |
| tci->command.header.commandId = CMD_ID_TEE_UPGRADE_KEY; |
| tci->upgrade_key.key_to_upgrade.data = (uint32_t)keyin_map.sVirtualAddr; |
| tci->upgrade_key.key_to_upgrade.data_length = |
| (uint32_t)keyin_map.sVirtualLen; |
| tci->upgrade_key.upgrade_params.data = (uint32_t)param_map.sVirtualAddr; |
| tci->upgrade_key.upgrade_params.data_length = |
| (uint32_t)param_map.sVirtualLen; |
| tci->upgrade_key.upgraded_key.data = (uint32_t)keyout_map.sVirtualAddr; |
| tci->upgrade_key.upgraded_key.data_length = |
| (uint32_t)keyout_map.sVirtualLen; |
| |
| /* See if it worked. */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* Return the new blob, or NULL if upgrade wasn't needed after all. */ |
| newblobsz = tci->upgrade_key.upgraded_key.data_length; |
| upgraded_key->key_material = newblobsz ? kbuf : NULL; |
| upgraded_key->key_material_size = newblobsz; |
| |
| end: |
| if (key_to_upgrade1.buf) { |
| unmap_buffer(session_handle, key_to_upgrade1.buf.get(), &keyin_map); |
| } |
| |
| unmap_buffer(session_handle, serialized_params.buf.get(), ¶m_map); |
| unmap_buffer(session_handle, kbuf, &keyout_map); |
| if ((ret != KM_ERROR_OK) || !newblobsz) free(kbuf); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_Begin( |
| TEE_SessionHandle sessionHandle, |
| keymaster_purpose_t purpose, |
| const keymaster_key_blob_t* key, |
| const keymaster_key_param_set_t* params, |
| const keymaster_hw_auth_token_t* auth_token, |
| keymaster_key_param_set_t* out_params, |
| keymaster_operation_handle_t* operation_handle) |
| { |
| LOG_D("TEE_Begin"); |
| LOG_D("purpose = 0x%08x", purpose); |
| PRINT_PARAM_SET(params); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcBulkMap_t paramsInfo = {0, 0}; |
| mcBulkMap_t keyBlobInfo = {0, 0}; |
| mcBulkMap_t outParamsInfo = {0, 0}; |
| scoped_buf_ptr_t serializedData; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| uint8_t serialized_out_params[TEE_BEGIN_OUT_PARAMS_SIZE]; |
| struct operation *op = NULL; |
| /* create key_blob buffer to copy input key content */ |
| scoped_buf_ptr_t key_blob; |
| |
| /* Make valgrind happy. */ |
| memset(serialized_out_params, 0, TEE_BEGIN_OUT_PARAMS_SIZE); |
| |
| if (out_params != NULL) { |
| out_params->params = NULL; |
| out_params->length = 0; |
| } |
| |
| CHECK_NOT_NULL(tci); |
| CHECK_NOT_NULL(key); |
| CHECK_NOT_NULL(operation_handle); |
| |
| /* Check parameters are valid for begin */ |
| CHECK_RESULT_OK(check_params(params, op_begin_allowed_params, |
| sizeof(op_begin_allowed_params) / sizeof(keymaster_tag_t)) ); |
| |
| /* Serialize params */ |
| CHECK_RESULT_OK(km_serialize_params( |
| serializedData, params, false, 0, 0)); |
| |
| /* Map params */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| serializedData.buf.get(), serializedData.size, ¶msInfo) ); |
| |
| if ( key->key_material != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( copy_to_scoped_buf(key->key_material, key->key_material_size, key_blob) ); |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| key_blob.buf.get(), key_blob.size, &keyBlobInfo) ); |
| } |
| |
| /* Map serialized_out_params */ |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| serialized_out_params, TEE_BEGIN_OUT_PARAMS_SIZE, &outParamsInfo) ); |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_BEGIN; |
| tci->begin.purpose = purpose; |
| tci->begin.params.data = (uint32_t)paramsInfo.sVirtualAddr; |
| tci->begin.params.data_length = serializedData.size; |
| tci->begin.key_blob.data = (uint32_t)keyBlobInfo.sVirtualAddr; |
| tci->begin.key_blob.data_length = key->key_material_size; |
| if (out_params != NULL) { |
| tci->begin.out_params.data = (uint32_t)outParamsInfo.sVirtualAddr; |
| tci->begin.out_params.data_length = TEE_BEGIN_OUT_PARAMS_SIZE; |
| } else { |
| tci->begin.out_params.data = 0; |
| tci->begin.out_params.data_length = 0; |
| } |
| tci->begin.auth_challenge = auth_token ? auth_token->challenge : 0; |
| tci->begin.auth_user_id = auth_token ? auth_token->user_id : 0; |
| tci->begin.auth_authenticator_id = auth_token ? auth_token->authenticator_id : 0; |
| tci->begin.auth_authenticator_type = auth_token ? auth_token->authenticator_type : KM_HW_AUTH_TYPE_NONE; |
| tci->begin.auth_timestamp = auth_token ? auth_token->timestamp : 0; |
| memset(tci->begin.auth_mac, 0, 32); |
| if (auth_token && auth_token->mac.data_length) { |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| auth_token->mac.data_length == 32); |
| memcpy(tci->begin.auth_mac, auth_token->mac.data, 32); |
| } |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| /* Deserialize out_params */ |
| if (out_params != NULL) { |
| uint8_t *pos = serialized_out_params; |
| uint32_t remain = tci->begin.out_params.data_length; |
| if (remain > 0) { |
| CHECK_RESULT_OK(deserialize_param_set(out_params, &pos, &remain)); |
| } else { |
| out_params->params = NULL; |
| out_params->length = 0; |
| } |
| } |
| |
| /* Update operation handle */ |
| op = find_spare_operation_slot(session, tci->begin.handle); |
| op->algorithm = static_cast<keymaster_algorithm_t>(tci->begin.algorithm); |
| op->final_length = tci->begin.final_length; |
| *operation_handle = op->handle; |
| |
| end: |
| if (ret != KM_ERROR_OK) { |
| if (out_params != NULL) { |
| keymaster_free_param_set(out_params); |
| out_params->length = 0; |
| } |
| } |
| unmap_buffer(session_handle, serializedData.buf.get(), ¶msInfo); |
| |
| /* unmap key_blob */ |
| if (key_blob.buf) { |
| unmap_buffer(session_handle, key_blob.buf.get(), &keyBlobInfo); |
| } |
| |
| unmap_buffer(session_handle, serialized_out_params, &outParamsInfo); |
| |
| LOG_D("TEE_Begin exiting with %d", ret); |
| return ret; |
| } |
| |
| /** |
| * Maximum size of buffer to process internally in an update() operation. |
| * |
| * Longer messages are split up into chunks this size. |
| */ |
| #define INPUT_CHUNK_SIZE 4096*4 |
| |
| /** |
| * Process a chunk of input to an operation. |
| * |
| * @param session session pointer |
| * @param operation_handle operation handle |
| * @param paramsInfo serialized parameters, mapped |
| * @param data input data (not NULL) |
| * @param data_length length of \p data |
| * @param[out] output output if required (pre-allocated), or NULL |
| * @param[in,out] input_consumed input consumed (incremented) |
| * |
| * @return KM_ERROR_OK or error |
| */ |
| static keymaster_error_t update_chunk( |
| struct TEE_Session *session, const struct operation *op, |
| const mcBulkMap_t *param_map, |
| const mcBulkMap_t *input_map, size_t *input_consumed_r, |
| const mcBulkMap_t *output_map, size_t *output_used_r, |
| const keymaster_hw_auth_token_t *auth_token, |
| void *) |
| { |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| tci->command.header.commandId = CMD_ID_TEE_UPDATE; |
| tci->update.handle = op->handle; |
| tci->update.params.data = (uint32_t)param_map->sVirtualAddr; |
| tci->update.params.data_length = param_map->sVirtualLen; |
| tci->update.input.data = (uint32_t)input_map->sVirtualAddr; |
| tci->update.input.data_length = input_map->sVirtualLen; |
| tci->update.output.data = (uint32_t)output_map->sVirtualAddr; |
| tci->update.output.data_length = output_map->sVirtualLen; |
| tci->update.auth_challenge = auth_token ? auth_token->challenge : 0; |
| tci->update.auth_user_id = auth_token ? auth_token->user_id : 0; |
| tci->update.auth_authenticator_id = auth_token ? auth_token->authenticator_id : 0; |
| tci->update.auth_authenticator_type = auth_token ? auth_token->authenticator_type : KM_HW_AUTH_TYPE_NONE; |
| tci->update.auth_timestamp = auth_token ? auth_token->timestamp : 0; |
| memset(tci->update.auth_mac, 0, 32); |
| if (auth_token && auth_token->mac.data_length) { |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| auth_token->mac.data_length == 32); |
| memcpy(tci->update.auth_mac, auth_token->mac.data, 32); |
| } |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| *input_consumed_r = tci->update.input_consumed; |
| *output_used_r = tci->update.output.data_length; |
| end: |
| return ret; |
| } |
| |
| struct finish_chunk_ctx { |
| mcBulkMap_t signature_map; |
| }; |
| |
| /** |
| * Process a chunk of input to an operation. |
| * |
| * @param session session pointer |
| * @param operation_handle operation handle |
| * @param paramsInfo serialized parameters, mapped |
| * @param data input data (not NULL) |
| * @param data_length length of \p data |
| * @param[out] output output if required (pre-allocated), or NULL |
| * @param[in,out] input_consumed input consumed (incremented) |
| * @param ctx pointer to @c finish_chunk_ctx structure |
| * |
| * @return KM_ERROR_OK or error |
| */ |
| static keymaster_error_t final_chunk( |
| struct TEE_Session *session, const struct operation *op, |
| const mcBulkMap_t *param_map, |
| const mcBulkMap_t *input_map, size_t *input_consumed_r, |
| const mcBulkMap_t *output_map, size_t *output_used_r, |
| const keymaster_hw_auth_token_t *auth_token, |
| void *ctx) |
| { |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| struct finish_chunk_ctx *fin = |
| static_cast<struct finish_chunk_ctx *>(ctx); |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| tci->command.header.commandId = CMD_ID_TEE_FINISH; |
| tci->finish.handle = op->handle; |
| tci->finish.params.data = (uint32_t)param_map->sVirtualAddr; |
| tci->finish.params.data_length = param_map->sVirtualLen; |
| tci->finish.input.data = (uint32_t)input_map->sVirtualAddr; |
| tci->finish.input.data_length = input_map->sVirtualLen; |
| tci->finish.signature.data = (uint32_t)fin->signature_map.sVirtualAddr; |
| tci->finish.signature.data_length = fin->signature_map.sVirtualLen; |
| tci->finish.output.data = (uint32_t)output_map->sVirtualAddr; |
| tci->finish.output.data_length = output_map->sVirtualLen; |
| tci->finish.auth_challenge = auth_token ? auth_token->challenge : 0; |
| tci->finish.auth_user_id = auth_token ? auth_token->user_id : 0; |
| tci->finish.auth_authenticator_id = auth_token ? auth_token->authenticator_id : 0; |
| tci->finish.auth_authenticator_type = auth_token ? auth_token->authenticator_type : KM_HW_AUTH_TYPE_NONE; |
| tci->finish.auth_timestamp = auth_token ? auth_token->timestamp : 0; |
| memset(tci->finish.auth_mac, 0, 32); |
| if (auth_token && auth_token->mac.data_length) { |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| auth_token->mac.data_length == 32); |
| memcpy(tci->finish.auth_mac, auth_token->mac.data, 32); |
| } |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| *input_consumed_r = input_map->sVirtualLen; |
| *output_used_r = tci->finish.output.data_length; |
| end: |
| return ret; |
| } |
| |
| typedef keymaster_error_t chunk_submit_func( |
| struct TEE_Session *session, const struct operation *op, |
| const mcBulkMap_t *param_map, |
| const mcBulkMap_t *input_map, size_t *input_consumed_r, |
| const mcBulkMap_t *output_map, size_t *output_used_r, |
| const keymaster_hw_auth_token_t *auth_token, |
| void *ctx); |
| |
| /** |
| * @param session pointer to session structure |
| * @param op pointer to operation in progress |
| * @param at_least_one always submit at least one chunk, even if it's empty |
| * @param params parameters to pass on to submission function |
| * @param input pointer to input buffer/length |
| * @param input_consumed_r where to put the amount of input actually consumed |
| * @param output pointer to the output buffer |
| * @param output_limit one-past-the-end of the available output buffer |
| * @param output_used_r where to put the amount of output actually written |
| * @param submit function to call to submit most chunks |
| * @param submit_list function to call to submit the final chunk |
| * @param ctx uninterpreted context pointer for the @p submit functions |
| * |
| * @return @c KM_ERROR_OK or error |
| * |
| * General-purpose data chunking function. |
| * |
| * There's a (not very generous) limit to the amount of data we can squeeze |
| * through the interface to our TA, and our caller isn't aware of it. We can |
| * sort out this mess by splitting the input into smaller pieces and submitting |
| * them one at a time. This function does that. |
| * |
| * The @p params contain parameter tags and values to be passed through. These |
| * are important because they can carry `additional authenticated data' for AEAD |
| * schemes, which can also be arbitrarily large, and require their own special |
| * handling. In particular, there's an ordering rule: no more AAD can be |
| * submitted once any of the main payload has been submitted. This function |
| * handles all of that. |
| * |
| * It submits individual chunks other than the last to its @p submit function |
| * for actual submission to the TA; the last is instead passed to |
| * @p submit_last. |
| * |
| * Usually, if there is no @p input data, and no AAD in the @p params, then this |
| * function doesn't do anything, and in particular doesn't submit any chunks. |
| * If @p at_least_one is true, then at least one chunk is submitted, even if |
| * it's entirely empty; obviously, in this case, it will be sent to the |
| * @p submit_last function. |
| * |
| * This function takes responsibility for setting up the necessary shared-memory |
| * mappings. |
| */ |
| static keymaster_error_t split_update_chunks( |
| struct TEE_Session *session, const struct operation *op, |
| bool at_least_one, const keymaster_key_param_set_t *params, |
| const keymaster_blob_t *input, size_t *input_consumed_r, |
| uint8_t *output, const uint8_t *output_limit, size_t *output_used_r, |
| const keymaster_hw_auth_token_t *auth_token, |
| chunk_submit_func *submit, chunk_submit_func *submit_last, |
| void *ctx) |
| { |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| const uint8_t *inp, *inp_limit; |
| uint8_t *output_window = NULL; |
| keymaster_key_param_t *param_buf = NULL, *aad_param; |
| size_t nparams = params ? params->length : 0; |
| keymaster_key_param_set_t my_params; |
| mcBulkMap_t param_map = { 0, 0 }; |
| mcBulkMap_t input_map = { 0, 0 }; |
| mcBulkMap_t output_map = { 0, 0 }; |
| scoped_buf_ptr_t serialized_params; |
| scoped_buf_ptr_t aad_window; |
| scoped_buf_ptr_t input_window; |
| bool must_reserialize_params = params; |
| |
| LOG_D("split_update_chunks"); |
| |
| /* Get the input buffer, if there is one. (There might not be, say |
| * because we're just pushing additional data at an AAD scheme.) |
| */ |
| if (!input || !input->data_length) |
| inp = inp_limit = NULL; |
| else { |
| inp = input->data; |
| inp_limit = inp + input->data_length; |
| } |
| |
| /* Sort out the paramters. This only gets complicated if we're doing |
| * AAD, i.e., if `KM_TAG_ASSOCIATED_DATA' is present. |
| */ |
| const uint8_t *aad, *aad_limit; |
| aad_param = NULL; |
| for (size_t i = 0; i < nparams; i++) { |
| if (params->params[i].tag == KM_TAG_ASSOCIATED_DATA) { |
| aad_param = ¶ms->params[i]; |
| break; |
| } |
| } |
| if (!aad_param) { |
| aad = aad_limit = NULL; |
| if (params) |
| my_params = *params; |
| else { |
| my_params.params = NULL; |
| my_params.length = 0; |
| } |
| } else { |
| |
| /* Yes, we have AAD. Make a copy of the parameter vector, but move |
| * the AAD parameter to the end so that we can strip it out easily |
| * later. |
| */ |
| param_buf = |
| (keymaster_key_param_t *)malloc(nparams*sizeof(*param_buf)); |
| if (!param_buf) { |
| ret = KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| goto end; |
| } |
| size_t aad_off = aad_param - params->params; |
| memcpy(param_buf, params->params, aad_off*sizeof(*param_buf)); |
| memcpy(param_buf + aad_off, params->params + aad_off + 1, |
| (nparams - aad_off - 1)*sizeof(*param_buf)); |
| param_buf[nparams - 1] = *aad_param; |
| aad_param = ¶m_buf[nparams - 1]; |
| |
| /* Now dig the data buffer pointers out. */ |
| if (!aad_param->blob.data_length) |
| aad = aad_limit = NULL; |
| else { |
| aad = aad_param->blob.data; |
| aad_limit = aad + aad_param->blob.data_length; |
| } |
| my_params.params = param_buf; |
| my_params.length = nparams; |
| } |
| |
| /* Now we get to do the main work. */ |
| while (at_least_one || aad || inp) { |
| bool maybe_last_time = true; |
| size_t budget = INPUT_CHUNK_SIZE; |
| |
| at_least_one = false; |
| |
| /* First, handle the parameters. This is actually the fiddliest bit. |
| * If there's AAD still to come, or we haven't done this part, then |
| * we serialize the parameters. This ensures that we submit the AAD |
| * before the main input data. |
| * |
| * Optimization for later: serialize the parameters once, but keep |
| * track of where the AAD buffer is, and just hack that part on each |
| * iteration. |
| */ |
| if (must_reserialize_params) { |
| PRINT_PARAM_SET(&my_params); |
| unmap_buffer(session_handle, |
| serialized_params.buf.get(), ¶m_map); |
| if (!aad_param) { |
| must_reserialize_params = false; |
| aad_window.buf.reset(); |
| } else { |
| size_t n = aad ? aad_limit - aad : 0; |
| if (n > budget) { |
| n = budget; |
| maybe_last_time = false; |
| } |
| CHECK_RESULT_OK(copy_to_scoped_buf(aad, n, aad_window)); |
| aad_param->blob.data = aad_window.buf.get(); |
| aad_param->blob.data_length = n; |
| budget -= n; |
| aad += n; |
| } |
| CHECK_RESULT_OK(km_serialize_params(serialized_params, |
| &my_params, false, 0, 0)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| serialized_params.buf.get(), serialized_params.size, |
| ¶m_map)); |
| if (aad_param && aad >= aad_limit) { |
| aad = aad_limit = NULL; |
| aad_param = NULL; |
| my_params.length--; |
| /* We shall reserialize without the parameters next time |
| * through (if there is one), for the last time. |
| */ |
| } |
| } |
| |
| /* Now deal with the message input, assuming we have any budget |
| * available for this. We certainly don't want any old buffer from |
| * last time hanging around. If we're using up the last of our input |
| * then switch in the other submit function. |
| */ |
| unmap_buffer(session_handle, input_window.buf.get(), &input_map); |
| if (inp && budget) { |
| size_t n = inp_limit - inp; |
| if (n > budget) { |
| n = budget; |
| maybe_last_time = false; |
| } |
| budget -= n; |
| CHECK_RESULT_OK(copy_to_scoped_buf(inp, n, input_window)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| input_window.buf.get(), n, &input_map)); |
| } |
| |
| /* Finally, arrange some output. There are two interesting cases. |
| * |
| * If we're in the middle of an operation, then the long pole is AES, |
| * which might have a (nearly complete) block (16 bytes) up its |
| * sleeve from before this operation; nothing else (currently!) can |
| * produce output at this time. |
| * |
| * On the other hand, if we're at the end of our input buffer, we |
| * should hand over the whole of the caller's output buffer because |
| * it was presumably provided for some good reason. |
| */ |
| unmap_buffer(session_handle, output_window, &output_map); |
| if (output) { |
| size_t n = output_limit - output; |
| if (!maybe_last_time) { |
| size_t avail = input_window.size + 16; |
| if (n > avail) n = avail; |
| } |
| output_window = output; |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| output_window, n, &output_map)); |
| } |
| |
| /* Push the next chunk through the machinery. At this point, |
| * `maybe_last_time' is definitely right. |
| */ |
| size_t input_consumed = 0; |
| size_t output_used = 0; |
| CHECK_RESULT_OK((maybe_last_time ? submit_last : submit)(session, op, |
| ¶m_map, |
| &input_map, &input_consumed, |
| &output_map, &output_used, |
| auth_token, |
| ctx)); |
| |
| /* Advance the input. (There doesn't seem to be a way for us to |
| * refuse to accept some of the AAD.) |
| */ |
| if (input_consumed_r) |
| *input_consumed_r += input_consumed; |
| if (inp) { |
| inp += input_consumed; |
| if (inp >= inp_limit) |
| inp = inp_limit = NULL; |
| } |
| |
| /* Advance the output. */ |
| if (output_used_r) |
| *output_used_r += output_used; |
| if (output) |
| output += output_used; |
| } |
| |
| end: |
| unmap_buffer(session_handle, serialized_params.buf.get(), ¶m_map); |
| unmap_buffer(session_handle, input_window.buf.get(), &input_map); |
| unmap_buffer(session_handle, output_window, &output_map); |
| free(param_buf); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_Update( |
| TEE_SessionHandle sessionHandle, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* params, |
| const keymaster_blob_t* input, |
| const keymaster_hw_auth_token_t* auth_token, |
| size_t* input_consumed, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) |
| { |
| LOG_D("TEE_Update"); |
| PRINT_PARAM_SET(params); |
| PRINT_BLOB(input); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| size_t output_used = 0; |
| struct operation *op = NULL; |
| |
| CHECK_RESULT_OK(lookup_operation(session, operation_handle, &op)); |
| |
| /* No output parameters */ |
| if (out_params != NULL) { |
| out_params->params = NULL; |
| out_params->length = 0; |
| } |
| |
| CHECK_NOT_NULL(input_consumed); |
| *input_consumed = 0; |
| |
| /* Check parameters are valid for update */ |
| CHECK_RESULT_OK(check_params(params, op_allowed_params, |
| sizeof(op_allowed_params) / sizeof(keymaster_tag_t)) ); |
| |
| /* Allocate output buffer if required. */ |
| if (output != NULL) { |
| if (op->algorithm == KM_ALGORITHM_AES) { |
| output->data_length = input->data_length + 16; // up to one more block |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&output->data, output->data_length)); |
| } else if (op->algorithm == KM_ALGORITHM_TRIPLE_DES) { |
| output->data_length = input->data_length + 8; // up to one more block |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&output->data, output->data_length)); |
| } else { |
| /* No output. */ |
| output->data = NULL; |
| output->data_length = 0; |
| } |
| } |
| |
| CHECK_RESULT_OK(split_update_chunks(session, op, false, |
| params, input, input_consumed, |
| output ? const_cast</*unconst*/ uint8_t *>(output->data) : NULL, |
| output ? output->data + output->data_length : NULL, |
| &output_used, auth_token, update_chunk, update_chunk, NULL)); |
| if (output) |
| output->data_length = output_used; |
| |
| end: |
| if (ret != KM_ERROR_OK) { |
| // free the output->data if it is allocated |
| if (output != NULL) { |
| free((void*)output->data); |
| output->data = NULL; |
| output->data_length = 0; |
| } |
| release_operation_slot(session, op); |
| } |
| |
| LOG_D("TEE_Update exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_Finish( |
| TEE_SessionHandle sessionHandle, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* params, |
| const keymaster_blob_t* input, |
| const keymaster_blob_t* signature, |
| const keymaster_hw_auth_token_t* auth_token, |
| keymaster_key_param_set_t* out_params, |
| keymaster_blob_t* output) |
| { |
| LOG_D("TEE_Finish"); |
| PRINT_PARAM_SET(params); |
| PRINT_BLOB(signature); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| scoped_buf_ptr_t signature1; |
| struct finish_chunk_ctx fin = { { 0, 0 } }; |
| struct operation *op = NULL; |
| size_t input_consumed = 0; |
| size_t output_used = 0; |
| |
| CHECK_RESULT_OK(lookup_operation(session, operation_handle, &op)); |
| |
| if ( signature != NULL ) { |
| /* Hack to ensure that non-writable memory can be mapped. */ |
| CHECK_RESULT_OK( |
| copy_to_scoped_buf(signature->data, signature->data_length, signature1)); |
| } |
| |
| if (output != NULL) { |
| output->data = NULL; |
| output->data_length = 0; |
| } |
| |
| /* No output parameters */ |
| if (out_params != NULL) { |
| out_params->params = NULL; |
| out_params->length = 0; |
| } |
| |
| /* Check parameters are valid for finish */ |
| CHECK_RESULT_OK(check_params(params, op_allowed_params, |
| sizeof(op_allowed_params) / sizeof(keymaster_tag_t)) ); |
| |
| /* Map signature buffer */ |
| if (signature1.buf) { |
| CHECK_RESULT_OK( map_buffer(session_handle, signature1.buf.get(), |
| signature1.size, &fin.signature_map) ); |
| } |
| |
| if (output != NULL) { |
| /* The output buffer is used for RSA-encrypt, RSA-decrypt, RSA-sign, |
| * AES-GCM-encrypt, AES or DES with PKCS7 padding, HMAC-sign, and |
| * ECDSA-sign. The `begin' request already told us how much extra |
| * space we'll need for the answer. |
| */ |
| output->data_length = op->final_length; |
| if (op->algorithm == KM_ALGORITHM_AES || |
| op->algorithm == KM_ALGORITHM_TRIPLE_DES) |
| { |
| output->data_length += (input ? input->data_length : 0); |
| } |
| |
| /* Allocate the output buffer. The caller must free this. */ |
| if (output->data_length != 0) { |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&output->data, |
| output->data_length)); |
| } |
| } |
| |
| CHECK_RESULT_OK(split_update_chunks(session, op, true, |
| params, input, &input_consumed, |
| output ? const_cast</*unconst*/ uint8_t *>(output->data) : NULL, |
| output ? output->data + output->data_length : NULL, |
| &output_used, auth_token, update_chunk, final_chunk, &fin)); |
| CHECK_TRUE(KM_ERROR_UNKNOWN_ERROR, |
| !input || input_consumed == input->data_length); |
| if (output) |
| output->data_length = output_used; |
| |
| end: |
| if (signature1.buf) { |
| unmap_buffer(session_handle, signature1.buf.get(), |
| &fin.signature_map); |
| } |
| |
| if (ret != KM_ERROR_OK) { |
| if (output != NULL) { |
| free((void*)output->data); |
| output->data = NULL; |
| output->data_length = 0; |
| } |
| } |
| |
| release_operation_slot(session, op); |
| LOG_D("TEE_Finish exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_Abort( |
| TEE_SessionHandle sessionHandle, |
| keymaster_operation_handle_t operation_handle) |
| { |
| LOG_D("TEE_Abort"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| struct operation *op = NULL; |
| |
| CHECK_RESULT_OK(lookup_operation(session, operation_handle, &op)); |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_ABORT; |
| tci->abort.handle = operation_handle; |
| |
| CHECK_RESULT_OK( transact(session_handle, tci) ); |
| |
| end: |
| release_operation_slot(session, op); |
| LOG_D("TEE_Abort exiting with %d", ret); |
| return ret; |
| } |
| |
| static keymaster_error_t parse_SecureKeyWrapper( |
| const uint8_t **encrypted_transport_key, size_t *encrypted_transport_key_len, |
| uint8_t *IV, size_t *IV_len, |
| const uint8_t **key_description, size_t *key_description_len, |
| uint32_t *key_format, |
| const uint8_t **key_authlist, size_t *key_authlist_len, |
| const uint8_t **encrypted_key, size_t *encrypted_key_len, |
| uint8_t *tag, |
| const uint8_t* data, size_t len) |
| { |
| keymaster_error_t ret = KM_ERROR_OK; |
| size_t sublen; |
| const uint8_t *p = data; |
| |
| // SEQUENCE |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| der_read_sequence_length(&sublen, &p, &len) && len == sublen); |
| |
| // INTEGER 0 (version) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| len >= 3 && p[0] == 0x02 && p[1] == 1 && p[2] == 0); |
| p += 3; len -= 3; |
| |
| // OCTET STRING (128 <= length < 65536) (encryptedTransportKey) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| der_read_octet_string_length(encrypted_transport_key_len, &p, &len)); |
| *encrypted_transport_key = p; |
| p += *encrypted_transport_key_len; len -= *encrypted_transport_key_len; |
| |
| // OCTET STRING (length <= 16) (initializationVector) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| len >= 2 && p[0] == 0x04 && p[1] <= 16); |
| *IV_len = p[1]; |
| p += 2; len -= 2; |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| len >= *IV_len); |
| memcpy(IV, p, *IV_len); |
| p += *IV_len; len -= *IV_len; |
| |
| // SEQUENCE (keyDescription) |
| *key_description = p; |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| der_read_sequence_length(&sublen, &p, &len)); |
| *key_description_len = 2 + sublen; |
| // INTEGER (length < 128) (KeyFormat) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| len >= 3 && p[0] == 0x02 && p[1] == 1); |
| *key_format = p[2]; |
| p += 3; len -= 3; |
| *key_authlist = p; |
| *key_authlist_len = sublen - 3; |
| p += *key_authlist_len; len -= *key_authlist_len; |
| |
| // OCTET STRING (encryptedKey) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| der_read_octet_string_length(encrypted_key_len, &p, &len)); |
| *encrypted_key = p; |
| p += *encrypted_key_len; len -= *encrypted_key_len; |
| |
| // OCTET STRING, length 16 (tag) |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, |
| len == 18 && p[0] == 0x04 && p[1] == 16); |
| memcpy(tag, p + 2, 16); |
| |
| end: |
| return ret; |
| } |
| |
| keymaster_error_t TEE_ImportWrappedKey( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_blob_t* wrapped_key_data, |
| const keymaster_key_blob_t* wrapping_key_blob, |
| const uint8_t* masking_key, |
| const keymaster_key_param_set_t* unwrapping_params, |
| uint64_t password_sid, |
| uint64_t biometric_sid, |
| keymaster_key_blob_t* key_blob, |
| keymaster_key_characteristics_t* key_characteristics) |
| { |
| LOG_D("TEE_ImportWrappedKey"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| uint32_t key_format = 0; |
| uint32_t key_type = 0; |
| uint64_t sid = 0; |
| const uint8_t *key_authlist; |
| size_t key_authlist_len; |
| uint8_t *key_params = NULL; |
| size_t key_params_len; |
| const uint8_t *encrypted_transport_key = NULL; |
| size_t encrypted_transport_key_len; |
| uint8_t *IV = tci->import_wrapped_key.initialization_vector; |
| size_t IV_len = 0; |
| const uint8_t *key_description = NULL; |
| size_t key_description_len; |
| const uint8_t *encrypted_key = NULL; |
| size_t encrypted_key_len; |
| uint8_t *tag = tci->import_wrapped_key.tag; |
| uint8_t *key_chars = NULL; |
| scoped_buf_ptr_t serialized_unwrap_params; |
| uint8_t *params = NULL; |
| size_t params_len; |
| uint8_t *wrap = NULL; |
| size_t wrap_len; |
| size_t ETK_offset, KD_offset, EK_offset, WKB_offset; |
| mcBulkMap_t params_Info = {0,0}; |
| mcBulkMap_t wrap_Info = {0,0}; |
| mcBulkMap_t key_blob_Info = {0,0}; |
| mcBulkMap_t key_characteristics_Info = {0,0}; |
| size_t max_charsz = 0; |
| |
| /* 1. Parse wrapped_key_data */ |
| CHECK_RESULT_OK(parse_SecureKeyWrapper( |
| &encrypted_transport_key, &encrypted_transport_key_len, |
| IV, &IV_len, |
| &key_description, &key_description_len, |
| &key_format, |
| &key_authlist, &key_authlist_len, |
| &encrypted_key, &encrypted_key_len, |
| tag, |
| wrapped_key_data->data, wrapped_key_data->data_length)); |
| CHECK_RESULT_OK(parse_AuthorizationList( |
| &key_params, &key_params_len, |
| &key_type, &sid, |
| key_authlist, key_authlist_len)); |
| CHECK_TRUE(KM_ERROR_INVALID_ARGUMENT, key_type != 0); |
| |
| /* 2. Check parameters are valid for unwrapping, and serialize them */ |
| CHECK_RESULT_OK(check_params(unwrapping_params, op_begin_allowed_params, |
| sizeof(op_begin_allowed_params)/sizeof(keymaster_tag_t))); |
| CHECK_RESULT_OK(km_serialize_params( |
| serialized_unwrap_params, unwrapping_params, false, 0, 0)); |
| |
| /* 3. Combine buffers */ |
| ETK_offset = key_params_len; |
| KD_offset = ETK_offset + encrypted_transport_key_len; |
| EK_offset = KD_offset + key_description_len; |
| params_len = EK_offset + encrypted_key_len; |
| CHECK_RESULT_OK(km_alloc(¶ms, params_len)); |
| memcpy(params, key_params, key_params_len); |
| memcpy(params + ETK_offset, encrypted_transport_key, encrypted_transport_key_len); |
| memcpy(params + KD_offset, key_description, key_description_len); |
| memcpy(params + EK_offset, encrypted_key, encrypted_key_len); |
| WKB_offset = serialized_unwrap_params.size; |
| wrap_len = WKB_offset + wrapping_key_blob->key_material_size; |
| CHECK_RESULT_OK(km_alloc(&wrap, wrap_len)); |
| memcpy(wrap, serialized_unwrap_params.buf.get(), serialized_unwrap_params.size); |
| memcpy(wrap + WKB_offset, wrapping_key_blob->key_material, wrapping_key_blob->key_material_size); |
| |
| /* 4. Allocate memory for key_blob. We don't know the key type or size, as |
| * they are encrypted, so we assume the worst case (RSA-4096). */ |
| key_blob->key_material = NULL; |
| key_blob->key_material_size = key_blob_max_size( |
| KM_ALGORITHM_RSA, 4096, serialized_unwrap_params.size); |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&key_blob->key_material, key_blob->key_material_size)); |
| |
| /* 5. Allocate memory for the serialized key characteristics */ |
| if (key_characteristics) { |
| max_charsz = key_blob->key_material_size; |
| CHECK_RESULT_OK(km_alloc(&key_chars, max_charsz)); |
| } |
| |
| /* 6. Map buffers */ |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| params, params_len, ¶ms_Info)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| wrap, wrap_len, &wrap_Info)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| key_blob->key_material, key_blob->key_material_size, &key_blob_Info)); |
| if (key_characteristics) { |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| key_chars, max_charsz, &key_characteristics_Info)); |
| } |
| |
| /* 7. Prepare TA command */ |
| tci->command.header.commandId = CMD_ID_TEE_IMPORT_WRAPPED_KEY; |
| tci->import_wrapped_key.key_format = key_format; |
| tci->import_wrapped_key.key_type = key_type; |
| tci->import_wrapped_key.params.data = (uint32_t)params_Info.sVirtualAddr; |
| tci->import_wrapped_key.params.data_length = params_len; |
| tci->import_wrapped_key.ETK_offset = ETK_offset; |
| tci->import_wrapped_key.KD_offset = KD_offset; |
| tci->import_wrapped_key.EK_offset = EK_offset; |
| tci->import_wrapped_key.IV_len = IV_len; |
| memcpy(tci->import_wrapped_key.masking_key, masking_key, 32); |
| tci->import_wrapped_key.wrap.data = (uint32_t)wrap_Info.sVirtualAddr; |
| tci->import_wrapped_key.wrap.data_length = wrap_len; |
| tci->import_wrapped_key.WKB_offset = WKB_offset; |
| tci->import_wrapped_key.password_sid = (sid & KM_HW_AUTH_TYPE_PASSWORD) ? password_sid : 0; |
| tci->import_wrapped_key.biometric_sid = (sid & KM_HW_AUTH_TYPE_FINGERPRINT) ? biometric_sid : 0; |
| tci->import_wrapped_key.key_blob.data = (uint32_t)key_blob_Info.sVirtualAddr; |
| tci->import_wrapped_key.key_blob.data_length = key_blob->key_material_size; |
| tci->import_wrapped_key.key_characteristics.data = (uint32_t)key_characteristics_Info.sVirtualAddr; |
| tci->import_wrapped_key.key_characteristics.data_length = max_charsz; |
| |
| /* 8. Call TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* 9. Update key blob length */ |
| key_blob->key_material_size = tci->import_wrapped_key.key_blob.data_length; |
| |
| /* 10. Give characteristics to caller */ |
| if (key_characteristics) { |
| CHECK_RESULT_OK(km_deserialize_characteristics( |
| key_characteristics, key_chars, max_charsz)); |
| } |
| |
| end: |
| unmap_buffer(session_handle, params, ¶ms_Info); |
| unmap_buffer(session_handle, wrap, &wrap_Info); |
| unmap_buffer(session_handle, key_blob->key_material, &key_blob_Info); |
| if (key_characteristics) { |
| unmap_buffer(session_handle, key_chars, &key_characteristics_Info); |
| } |
| free(key_params); |
| free(params); |
| free(wrap); |
| free(key_chars); |
| if (ret != KM_ERROR_OK) { |
| free((void*)key_blob->key_material); |
| key_blob->key_material = NULL; |
| key_blob->key_material_size = 0; |
| } |
| LOG_D("TEE_ImportWrappedKey exiting with %d", ret); |
| return ret; |
| } |
| |
| #ifndef NDEBUG |
| keymaster_error_t tee__set_debug_lies( |
| TEE_SessionHandle sessionHandle, |
| const bootimage_info_t *bootinfo) |
| { |
| LOG_D("%s: OS_VERSION = %" PRIu32"; OS_PATCHLEVEL = %" PRIu32 "; VENDOR_PATCHLEVEL = %" PRIu32 "; BOOT_PATCHLEVEL = %" PRIu32, |
| __func__, bootinfo->os_version, bootinfo->os_patchlevel, bootinfo->vendor_patchlevel, bootinfo->boot_patchlevel); |
| |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| keymaster_error_t ret = KM_ERROR_OK; |
| mcSessionHandle_t *session_handle = &session->sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| |
| tci->command.header.commandId = CMD_ID_TEE_SET_DEBUG_LIES; |
| tci->set_debug_lies.os_version = bootinfo->os_version; |
| tci->set_debug_lies.os_patchlevel = bootinfo->os_patchlevel; |
| tci->set_debug_lies.vendor_patchlevel = bootinfo->vendor_patchlevel; |
| tci->set_debug_lies.boot_patchlevel = bootinfo->boot_patchlevel; |
| |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| end: |
| return ret; |
| } |
| #endif /* #ifndef NDEBUG */ |
| |
| keymaster_error_t TEE_GetHmacSharingParameters( |
| TEE_SessionHandle sessionHandle, |
| keymaster_hmac_sharing_parameters_t *out_params) |
| { |
| LOG_D("TEE_GetHmacSharingParameters"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| |
| /* Parameter check */ |
| CHECK_NOT_NULL(out_params); |
| |
| /* Reset output parameters */ |
| memset(out_params, 0x0, sizeof(*out_params)); |
| |
| /* Prepare TA command */ |
| tci->command.header.commandId = CMD_ID_TEE_GET_HMAC_SHARING_PARAMETERS; |
| memset(&tci->get_hmac_sharing_parameters, 0x0, sizeof(tci->get_hmac_sharing_parameters)); |
| |
| /* Call TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* Update output parameters */ |
| if (tci->get_hmac_sharing_parameters.seed_length == KM_SHARING_PARAMETERS_SEED_LENGTH) { |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&out_params->seed.data, KM_SHARING_PARAMETERS_SEED_LENGTH)); |
| memcpy((void *)out_params->seed.data, tci->get_hmac_sharing_parameters.seed, KM_SHARING_PARAMETERS_NONCE_LENGTH); |
| out_params->seed.data_length = KM_SHARING_PARAMETERS_SEED_LENGTH; |
| } |
| |
| memcpy(out_params->nonce, tci->get_hmac_sharing_parameters.nonce, KM_SHARING_PARAMETERS_NONCE_LENGTH); |
| |
| end: |
| LOG_D("TEE_GetHmacSharingParameters exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_ComputeSharedMac( |
| TEE_SessionHandle sessionHandle, |
| const keymaster_hmac_sharing_parameters_set_t* sharing_params, |
| keymaster_blob_t *sharing_check) |
| { |
| LOG_D("TEE_ComputeSharedMac"); |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| |
| /* Parameter check */ |
| CHECK_NOT_NULL(sharing_params); |
| CHECK_NOT_NULL(sharing_check); |
| |
| if (sharing_params->length == 0) { |
| LOG_E("%s: wrong number of Keymasters", __func__); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| |
| if (sharing_params->length > KM_SHARING_PARAMETERS_MAX_NUMBER) { |
| LOG_E("%s: Maximum number of supported sharing paraeters is %u", __func__, |
| KM_SHARING_PARAMETERS_MAX_NUMBER); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| |
| for (size_t i = 0; i < sharing_params->length; i++) { |
| if (sharing_params->params[i].seed.data_length == 0) { |
| if (sharing_params->params[i].seed.data != NULL) { |
| LOG_E("%s: input parameters mismatch", __func__); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| } else if (sharing_params->params[i].seed.data_length == KM_SHARING_PARAMETERS_SEED_LENGTH) { |
| if (sharing_params->params[i].seed.data == NULL) { |
| LOG_E("%s: input parameters mismatch", __func__); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| } |
| else { |
| LOG_E("%s: wrong seed length", __func__); |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| } |
| |
| /* Reset output parameters */ |
| memset(sharing_check, 0x0, sizeof(*sharing_check)); |
| |
| /* Prepare TA command */ |
| memset(&tci->compute_shared_hmac, 0x0, sizeof(tci->compute_shared_hmac)); |
| tci->command.header.commandId = CMD_ID_TEE_COMPUTE_SHARED_HMAC; |
| tci->compute_shared_hmac.sharing_params_length = sharing_params->length; |
| for (size_t i = 0; i < sharing_params->length; i++) { |
| if (sharing_params->params[i].seed.data_length == KM_SHARING_PARAMETERS_SEED_LENGTH) { |
| tci->compute_shared_hmac.sharing_params[i].seed_length = KM_SHARING_PARAMETERS_SEED_LENGTH; |
| memcpy(tci->compute_shared_hmac.sharing_params[i].seed, sharing_params->params[i].seed.data, KM_SHARING_PARAMETERS_SEED_LENGTH); |
| } |
| memcpy(tci->compute_shared_hmac.sharing_params[i].nonce, sharing_params->params[i].nonce, KM_SHARING_PARAMETERS_NONCE_LENGTH); |
| } |
| |
| /* Call TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* Update output parameters */ |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&sharing_check->data, KM_SHARING_CHECK_LENGTH)); |
| memcpy((void *)sharing_check->data, tci->compute_shared_hmac.sharing_check, KM_SHARING_CHECK_LENGTH); |
| sharing_check->data_length = KM_SHARING_CHECK_LENGTH; |
| |
| end: |
| LOG_D("TEE_ComputeSharedMac exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_VerifyAuthorization( |
| TEE_SessionHandle sessionHandle, |
| keymaster_operation_handle_t operation_handle, |
| const keymaster_key_param_set_t* parameters_to_verify, |
| const keymaster_hw_auth_token_t* hw_auth_token, |
| keymaster_verification_token_t* verification_token) |
| { |
| LOG_D("TEE_VerifyAuthorization"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| struct TEE_Session *session = (struct TEE_Session *)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| |
| scoped_buf_ptr_t serialized_params_to_verify; |
| mcBulkMap_t params_to_verify_info = {0, 0}; |
| |
| uint8_t *serialized_params_verified = NULL; |
| mcBulkMap_t params_verified_info = {0, 0}; |
| |
| uint8_t *pos = NULL; |
| uint32_t remain = 0; |
| |
| |
| /* Parameter check */ |
| CHECK_NOT_NULL(parameters_to_verify); |
| CHECK_NOT_NULL(verification_token); |
| |
| /* Reset output parameters */ |
| memset(verification_token, 0x0, sizeof(*verification_token)); |
| |
| /* Serialization and mapping */ |
| CHECK_RESULT_OK(km_serialize_params( |
| serialized_params_to_verify, parameters_to_verify, false, 0, 0)); |
| |
| CHECK_RESULT_OK( map_buffer(session_handle, |
| serialized_params_to_verify.buf.get(), serialized_params_to_verify.size, ¶ms_to_verify_info) ); |
| |
| |
| /* Allocate memory for output key parameter set */ |
| /* size no more than input set */ |
| CHECK_RESULT_OK(km_alloc(&serialized_params_verified, serialized_params_to_verify.size)); |
| CHECK_RESULT_OK(map_buffer(session_handle, |
| serialized_params_verified, serialized_params_to_verify.size, ¶ms_verified_info)); |
| |
| /* Prepare TA command */ |
| memset(&tci->verify_authorization, 0x0, sizeof(tci->verify_authorization)); |
| tci->command.header.commandId = CMD_ID_TEE_VERIFY_AUTHORIZATION; |
| tci->verify_authorization.operation_handle = operation_handle; |
| tci->verify_authorization.params_to_verify.data = (uint32_t)params_to_verify_info.sVirtualAddr; |
| tci->verify_authorization.params_to_verify.data_length = params_to_verify_info.sVirtualLen; |
| |
| if ((hw_auth_token != NULL) && (hw_auth_token->mac.data_length == 32) && (hw_auth_token->authenticator_type != KM_HW_AUTH_TYPE_NONE)) { |
| tci->verify_authorization.auth_challenge = hw_auth_token->challenge; |
| tci->verify_authorization.auth_user_id = hw_auth_token->user_id; |
| tci->verify_authorization.auth_authenticator_id = hw_auth_token->authenticator_id; |
| tci->verify_authorization.auth_authenticator_type = hw_auth_token->authenticator_type; |
| tci->verify_authorization.auth_timestamp = hw_auth_token->timestamp; |
| memcpy(tci->verify_authorization.auth_mac, hw_auth_token->mac.data, 32); |
| } |
| else { |
| tci->verify_authorization.auth_authenticator_type = KM_HW_AUTH_TYPE_NONE; |
| } |
| |
| tci->verify_authorization.ver_params.data = (uint32_t)params_verified_info.sVirtualAddr; |
| tci->verify_authorization.ver_params.data_length = params_verified_info.sVirtualLen; |
| |
| /* Call TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| /* Handling output parameters */ |
| pos = serialized_params_verified; |
| remain = tci->verify_authorization.ver_params.data_length; |
| if (remain > 0) { |
| CHECK_RESULT_OK(deserialize_param_set(&verification_token->parameters_verified, &pos, &remain)); |
| } |
| verification_token->challenge = tci->verify_authorization.ver_challenge; |
| verification_token->timestamp = tci->verify_authorization.ver_timestamp; |
| verification_token->security_level = (keymaster_security_level_t)tci->verify_authorization.ver_security_level; |
| CHECK_RESULT_OK(km_alloc((uint8_t**)&verification_token->mac.data, 32)); |
| memcpy((void *)verification_token->mac.data, tci->verify_authorization.ver_mac, 32); |
| verification_token->mac.data_length = 32; |
| |
| end: |
| if (serialized_params_to_verify.buf) { |
| unmap_buffer(session_handle, serialized_params_to_verify.buf.get(), ¶ms_to_verify_info); |
| } |
| |
| unmap_buffer(session_handle, serialized_params_verified, ¶ms_verified_info); |
| |
| free(serialized_params_verified); |
| |
| LOG_D("TEE_VerifyAuthorization exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_DestroyAttestationIds( |
| TEE_SessionHandle sessionHandle) |
| { |
| LOG_D("TEE_DestroyAttestationIds"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| struct TEE_Session *session = (struct TEE_Session*)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_DESTROY_ATTESTATION_IDS; |
| |
| /* Talk to TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| end: |
| LOG_D("TEE_DestroyAttestationIds exiting with %d", ret); |
| return ret; |
| } |
| |
| keymaster_error_t TEE_EarlyBootEnded( |
| TEE_SessionHandle sessionHandle) |
| { |
| LOG_D("TEE_EarlyBootEnded"); |
| |
| keymaster_error_t ret = KM_ERROR_OK; |
| |
| struct TEE_Session *session = (struct TEE_Session*)sessionHandle; |
| tciMessage_ptr tci = session->pTci; |
| mcSessionHandle_t* session_handle = &session->sessionHandle; |
| |
| /* Update TCI buffer */ |
| tci->command.header.commandId = CMD_ID_TEE_EARLY_BOOT_ENDED; |
| |
| /* Talk to TA */ |
| CHECK_RESULT_OK(transact(session_handle, tci)); |
| |
| end: |
| LOG_D("TEE_EarlyBootEnded exiting with %d", ret); |
| return ret; |
| } |