| /* |
| * Copyright 2015 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 "integrity_assured_key_blob.h" |
| |
| #include <assert.h> |
| |
| #include <new> |
| |
| #include <openssl/hmac.h> |
| #include <openssl/mem.h> |
| |
| #include <keymaster/android_keymaster_utils.h> |
| #include <keymaster/authorization_set.h> |
| |
| #include "openssl_err.h" |
| |
| namespace keymaster { |
| |
| static const uint8_t BLOB_VERSION = 0; |
| static const size_t HMAC_SIZE = 8; |
| static const char HMAC_KEY[] = "IntegrityAssuredBlob0"; |
| |
| inline size_t min(size_t a, size_t b) { |
| if (a < b) |
| return a; |
| return b; |
| } |
| |
| class HmacCleanup { |
| public: |
| HmacCleanup(HMAC_CTX* ctx) : ctx_(ctx) {} |
| ~HmacCleanup() { HMAC_CTX_cleanup(ctx_); } |
| |
| private: |
| HMAC_CTX* ctx_; |
| }; |
| |
| static keymaster_error_t ComputeHmac(const uint8_t* serialized_data, size_t serialized_data_size, |
| const AuthorizationSet& hidden, uint8_t hmac[HMAC_SIZE]) { |
| size_t hidden_bytes_size = hidden.SerializedSize(); |
| UniquePtr<uint8_t[]> hidden_bytes(new (std::nothrow) uint8_t[hidden_bytes_size]); |
| if (!hidden_bytes.get()) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| hidden.Serialize(hidden_bytes.get(), hidden_bytes.get() + hidden_bytes_size); |
| |
| HMAC_CTX ctx; |
| HMAC_CTX_init(&ctx); |
| const EVP_MD* md = EVP_sha256(); |
| if (!HMAC_Init_ex(&ctx, HMAC_KEY, sizeof(HMAC_KEY), md, NULL /* engine */)) |
| return TranslateLastOpenSslError(); |
| HmacCleanup cleanup(&ctx); |
| |
| uint8_t tmp[EVP_MAX_MD_SIZE]; |
| unsigned tmp_len; |
| if (!HMAC_Update(&ctx, serialized_data, serialized_data_size) || |
| !HMAC_Update(&ctx, hidden_bytes.get(), hidden_bytes_size) || // |
| !HMAC_Final(&ctx, tmp, &tmp_len)) |
| return TranslateLastOpenSslError(); |
| |
| assert(tmp_len >= HMAC_SIZE); |
| memcpy(hmac, tmp, min(HMAC_SIZE, tmp_len)); |
| |
| return KM_ERROR_OK; |
| } |
| |
| keymaster_error_t SerializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_material, |
| const AuthorizationSet& hidden, |
| const AuthorizationSet& hw_enforced, |
| const AuthorizationSet& sw_enforced, |
| KeymasterKeyBlob* key_blob) { |
| size_t size = 1 /* version */ + // |
| key_material.SerializedSize() + // |
| hw_enforced.SerializedSize() + // |
| sw_enforced.SerializedSize() + // |
| HMAC_SIZE; |
| |
| if (!key_blob->Reset(size)) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| uint8_t* p = key_blob->writable_data(); |
| *p++ = BLOB_VERSION; |
| p = key_material.Serialize(p, key_blob->end()); |
| p = hw_enforced.Serialize(p, key_blob->end()); |
| p = sw_enforced.Serialize(p, key_blob->end()); |
| |
| return ComputeHmac(key_blob->key_material, p - key_blob->key_material, hidden, p); |
| } |
| |
| keymaster_error_t DeserializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_blob, |
| const AuthorizationSet& hidden, |
| KeymasterKeyBlob* key_material, |
| AuthorizationSet* hw_enforced, |
| AuthorizationSet* sw_enforced) { |
| const uint8_t* p = key_blob.begin(); |
| const uint8_t* end = key_blob.end(); |
| |
| if (p > end || p + HMAC_SIZE > end) |
| return KM_ERROR_INVALID_KEY_BLOB; |
| |
| uint8_t computed_hmac[HMAC_SIZE]; |
| keymaster_error_t error = ComputeHmac(key_blob.begin(), key_blob.key_material_size - HMAC_SIZE, |
| hidden, computed_hmac); |
| if (error != KM_ERROR_OK) |
| return error; |
| |
| if (CRYPTO_memcmp(key_blob.end() - HMAC_SIZE, computed_hmac, HMAC_SIZE) != 0) |
| return KM_ERROR_INVALID_KEY_BLOB; |
| |
| if (*p != BLOB_VERSION) |
| return KM_ERROR_INVALID_KEY_BLOB; |
| ++p; |
| |
| if (!key_material->Deserialize(&p, end) || // |
| !hw_enforced->Deserialize(&p, end) || // |
| !sw_enforced->Deserialize(&p, end)) |
| return KM_ERROR_INVALID_KEY_BLOB; |
| |
| return KM_ERROR_OK; |
| } |
| |
| } // namespace keymaster; |