| /* |
| ** |
| ** Copyright 2018, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include "openssl_keymaster_enforcement.h" |
| |
| #include <assert.h> |
| |
| #include <openssl/cmac.h> |
| #include <openssl/evp.h> |
| #include <openssl/hmac.h> |
| #include <openssl/rand.h> |
| |
| #include <keymaster/km_openssl/ckdf.h> |
| #include <keymaster/km_openssl/openssl_err.h> |
| #include <keymaster/km_openssl/openssl_utils.h> |
| |
| #include <lib/hwkey/hwkey.h> |
| |
| namespace keymaster { |
| |
| namespace { |
| |
| constexpr const char* kSharedHmacLabel = "KeymasterSharedMac"; |
| constexpr const char* kMacVerificationString = "Keymaster HMAC Verification"; |
| constexpr const char* kAuthVerificationLabel = "Auth Verification"; |
| |
| // Size (in bytes) of the HBK used for UNIQUE_ID generation. |
| static const int kUniqueIdHbkSize = 32; |
| |
| // Label used for CKDF derivation of UNIQUE_ID HBK from KAK. |
| constexpr const char* kUniqueIdLabel = "UniqueID HBK 32B"; |
| |
| class EvpMdCtx { |
| public: |
| EvpMdCtx() { EVP_MD_CTX_init(&ctx_); } |
| ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); } |
| |
| EVP_MD_CTX* get() { return &ctx_; } |
| |
| private: |
| EVP_MD_CTX ctx_; |
| }; |
| |
| } // anonymous namespace |
| |
| bool OpenSSLKeymasterEnforcement::CreateKeyId( |
| const keymaster_key_blob_t& key_blob, |
| km_id_t* keyid) const { |
| EvpMdCtx ctx; |
| |
| uint8_t hash[EVP_MAX_MD_SIZE]; |
| unsigned int hash_len; |
| if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) && |
| EVP_DigestUpdate(ctx.get(), key_blob.key_material, |
| key_blob.key_material_size) && |
| EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { |
| assert(hash_len >= sizeof(*keyid)); |
| memcpy(keyid, hash, sizeof(*keyid)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| keymaster_error_t OpenSSLKeymasterEnforcement::GetHmacSharingParameters( |
| HmacSharingParameters* params) { |
| if (!have_saved_params_) { |
| saved_params_.seed = {}; |
| RAND_bytes(saved_params_.nonce, 32); |
| have_saved_params_ = true; |
| } |
| params->seed = saved_params_.seed; |
| memcpy(params->nonce, saved_params_.nonce, sizeof(params->nonce)); |
| return KM_ERROR_OK; |
| } |
| |
| namespace { |
| |
| DEFINE_OPENSSL_OBJECT_POINTER(HMAC_CTX); |
| |
| keymaster_error_t hmacSha256(const keymaster_key_blob_t& key, |
| const keymaster_blob_t data_chunks[], |
| size_t data_chunk_count, |
| KeymasterBlob* output) { |
| if (!output) |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| |
| unsigned digest_len = SHA256_DIGEST_LENGTH; |
| if (!output->Reset(digest_len)) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| HMAC_CTX_Ptr ctx(HMAC_CTX_new()); |
| if (!HMAC_Init_ex(ctx.get(), key.key_material, key.key_material_size, |
| EVP_sha256(), nullptr /* engine*/)) { |
| return TranslateLastOpenSslError(); |
| } |
| |
| for (size_t i = 0; i < data_chunk_count; i++) { |
| auto& chunk = data_chunks[i]; |
| if (!HMAC_Update(ctx.get(), chunk.data, chunk.data_length)) { |
| return TranslateLastOpenSslError(); |
| } |
| } |
| |
| if (!HMAC_Final(ctx.get(), output->writable_data(), &digest_len)) { |
| return TranslateLastOpenSslError(); |
| } |
| |
| if (digest_len != output->data_length) |
| return KM_ERROR_UNKNOWN_ERROR; |
| |
| return KM_ERROR_OK; |
| } |
| |
| // Helpers for converting types to keymaster_blob_t, for easy feeding of |
| // hmacSha256. |
| template <typename T> |
| inline keymaster_blob_t toBlob(const T& t) { |
| return {reinterpret_cast<const uint8_t*>(&t), sizeof(t)}; |
| } |
| inline keymaster_blob_t toBlob(const char* str) { |
| return {reinterpret_cast<const uint8_t*>(str), strlen(str)}; |
| } |
| |
| // Perhaps these shoud be in utils, but the impact of that needs to be |
| // considered carefully. For now, just define it here. |
| inline bool operator==(const keymaster_blob_t& a, const keymaster_blob_t& b) { |
| if (!a.data_length && !b.data_length) |
| return true; |
| if (!(a.data && b.data)) |
| return a.data == b.data; |
| return (a.data_length == b.data_length && |
| !memcmp(a.data, b.data, a.data_length)); |
| } |
| |
| bool operator==(const HmacSharingParameters& a, |
| const HmacSharingParameters& b) { |
| return a.seed == b.seed && !memcmp(a.nonce, b.nonce, sizeof(a.nonce)); |
| } |
| |
| } // namespace |
| |
| keymaster_error_t OpenSSLKeymasterEnforcement::ComputeSharedHmac( |
| const HmacSharingParametersArray& params_array, |
| KeymasterBlob* sharingCheck) { |
| KeymasterKeyBlob kak; |
| keymaster_error_t kakError = GetKeyAgreementKey(&kak); |
| if (kakError != KM_ERROR_OK) { |
| return kakError; |
| } |
| |
| size_t num_chunks = params_array.num_params * 2; |
| UniquePtr<keymaster_blob_t[]> context_chunks( |
| new (std::nothrow) keymaster_blob_t[num_chunks]); |
| if (!context_chunks.get()) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| bool found_mine = false; |
| auto context_chunks_pos = context_chunks.get(); |
| for (auto& params : |
| array_range(params_array.params_array, params_array.num_params)) { |
| *context_chunks_pos++ = params.seed; |
| *context_chunks_pos++ = {params.nonce, sizeof(params.nonce)}; |
| found_mine = found_mine || params == saved_params_; |
| } |
| assert(context_chunks_pos - num_chunks == context_chunks.get()); |
| |
| if (!found_mine) |
| return KM_ERROR_INVALID_ARGUMENT; |
| |
| if (!hmac_key_.Reset(SHA256_DIGEST_LENGTH)) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| keymaster_error_t error = ckdf( |
| move(kak), |
| KeymasterBlob(reinterpret_cast<const uint8_t*>(kSharedHmacLabel), |
| strlen(kSharedHmacLabel)), |
| context_chunks.get(), num_chunks, // |
| &hmac_key_); |
| if (error != KM_ERROR_OK) |
| return error; |
| |
| keymaster_blob_t data = { |
| reinterpret_cast<const uint8_t*>(kMacVerificationString), |
| strlen(kMacVerificationString)}; |
| keymaster_blob_t data_chunks[] = {data}; |
| return hmacSha256(hmac_key_, data_chunks, 1, sharingCheck); |
| } |
| |
| VerifyAuthorizationResponse OpenSSLKeymasterEnforcement::VerifyAuthorization( |
| const VerifyAuthorizationRequest& request) { |
| // The only thing this implementation provides is timestamp and security |
| // level. Note that this is an acceptable implementation strategy for |
| // production use as well. Additional verification need only be provided by |
| // an implementation if it is interoperating with another implementation |
| // that requires more. |
| VerifyAuthorizationResponse response(request.message_version); |
| response.token.challenge = request.challenge; |
| response.token.timestamp = get_current_time_ms(); |
| response.token.security_level = SecurityLevel(); |
| keymaster_blob_t data_chunks[] = { |
| toBlob(kAuthVerificationLabel), |
| toBlob(response.token.challenge), |
| toBlob(response.token.timestamp), |
| toBlob(response.token.security_level), |
| {}, // parametersVerified |
| }; |
| response.error = hmacSha256(hmac_key_, data_chunks, 5, &response.token.mac); |
| |
| return response; |
| } |
| |
| keymaster_error_t OpenSSLKeymasterEnforcement::GetKeyAgreementKey( |
| KeymasterKeyBlob* kak) const { |
| uint32_t keySize = kKeyAgreementKeySize; |
| |
| if (!kak->Reset(keySize)) { |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| |
| int rc = hwkey_open(); |
| if (rc < 0) { |
| LOG_E("Failed to connect to hwkey: %d", rc); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } |
| hwkey_session_t hwkey_session = (hwkey_session_t)rc; |
| |
| rc = hwkey_get_keyslot_data(hwkey_session, "com.android.trusty.keymint.kak", |
| kak->writable_data(), &keySize); |
| |
| hwkey_close(hwkey_session); |
| |
| if (rc < 0) { |
| LOG_E("Getting KAK failed.\n", 0); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } |
| |
| if (keySize != kKeyAgreementKeySize) { |
| LOG_E("KAK has the wrong size: %zu != %zu.\n", keySize, |
| kKeyAgreementKeySize); |
| return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
| } |
| |
| return KM_ERROR_OK; |
| } |
| |
| keymaster_error_t OpenSSLKeymasterEnforcement::GetHmacKey( |
| keymaster_key_blob_t* key) const { |
| if ((key == nullptr) || (key->key_material == nullptr)) { |
| return KM_ERROR_UNEXPECTED_NULL_POINTER; |
| } |
| |
| if (hmac_key_.key_material_size != SHA256_DIGEST_LENGTH) { |
| return KM_ERROR_INVALID_ARGUMENT; |
| } |
| |
| memcpy((void*)key->key_material, hmac_key_.key_material, |
| hmac_key_.key_material_size); |
| key->key_material_size = hmac_key_.key_material_size; |
| |
| return KM_ERROR_OK; |
| } |
| |
| keymaster_error_t OpenSSLKeymasterEnforcement::GetUniqueIdKey( |
| KeymasterKeyBlob* key) const { |
| // Derive a unique ID HBK from the key agreement key. |
| KeymasterKeyBlob kak; |
| keymaster_error_t kakError = GetKeyAgreementKey(&kak); |
| if (kakError != KM_ERROR_OK) { |
| return kakError; |
| } |
| if (!key->Reset(kUniqueIdHbkSize)) { |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| } |
| |
| return ckdf(move(kak), |
| KeymasterBlob(reinterpret_cast<const uint8_t*>(kUniqueIdLabel), |
| strlen(kUniqueIdLabel)), |
| nullptr, 0, key); |
| } |
| |
| } // namespace keymaster |