android13/system/keymaster/key_blob_utils/auth_encrypted_key_blob.cpp

369 lines
16 KiB
C++

/*
* 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 <keymaster/key_blob_utils/auth_encrypted_key_blob.h>
#include <openssl/digest.h>
#include <openssl/evp.h>
#include <openssl/hkdf.h>
#include <keymaster/android_keymaster_utils.h>
#include <keymaster/authorization_set.h>
#include <keymaster/key_blob_utils/ocb_utils.h>
#include <keymaster/km_openssl/openssl_err.h>
#include <keymaster/logger.h>
#include <keymaster/random_source.h>
namespace keymaster {
namespace {
constexpr uint8_t kAesGcmDescriptor1[] = "AES-256-GCM-HKDF-SHA-256, version 1";
constexpr uint8_t kAesGcmDescriptor2[] = "AES-256-GCM-HKDF-SHA-256, version 2";
constexpr size_t kAesGcmNonceLength = 12;
constexpr size_t kAesGcmTagLength = 16;
constexpr size_t kAes256KeyLength = 256 / 8;
KmErrorOr<Buffer> generate_nonce(const RandomSource& random, size_t size) {
Buffer nonce;
if (!nonce.Reinitialize(size)) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
random.GenerateRandom(nonce.peek_write(), size);
nonce.advance_write(size);
return nonce;
}
KmErrorOr<Buffer> BuildDerivationInfo(const AuthEncryptedBlobFormat format, //
const AuthorizationSet& hw_enforced, //
const AuthorizationSet& sw_enforced, //
const AuthorizationSet& hidden,
const SecureDeletionData& secure_deletion_data) {
bool use_sdd = requiresSecureDeletion(format);
size_t info_len =
hidden.SerializedSize() + hw_enforced.SerializedSize() + sw_enforced.SerializedSize();
if (use_sdd) {
info_len += sizeof(kAesGcmDescriptor2) +
secure_deletion_data.factory_reset_secret.SerializedSize() +
secure_deletion_data.secure_deletion_secret.SerializedSize() +
sizeof(secure_deletion_data.key_slot);
} else {
info_len += sizeof(kAesGcmDescriptor1);
}
Buffer info(info_len);
info.write(use_sdd ? kAesGcmDescriptor2 : kAesGcmDescriptor1);
uint8_t* buf = info.peek_write();
const uint8_t* end = info.peek_write() + info.available_write();
buf = hidden.Serialize(buf, end);
buf = hw_enforced.Serialize(buf, end);
buf = sw_enforced.Serialize(buf, end);
if (use_sdd) {
buf = secure_deletion_data.factory_reset_secret.Serialize(buf, end);
buf = secure_deletion_data.secure_deletion_secret.Serialize(buf, end);
static_assert(std::is_same_v<decltype(secure_deletion_data.key_slot), uint32_t>);
buf = append_uint32_to_buf(buf, end, secure_deletion_data.key_slot);
}
if (!buf || buf != end || !info.advance_write(buf - info.peek_write())) {
LOG_S("Buffer management error", 0);
return KM_ERROR_UNKNOWN_ERROR;
}
return info;
}
KmErrorOr<Buffer> DeriveAesGcmKeyEncryptionKey(const AuthEncryptedBlobFormat format, //
const AuthorizationSet& hw_enforced, //
const AuthorizationSet& sw_enforced, //
const AuthorizationSet& hidden, //
const SecureDeletionData& secure_deletion_data, //
const KeymasterKeyBlob& master_key) {
Buffer prk(EVP_MAX_MD_SIZE);
size_t out_len = EVP_MAX_MD_SIZE;
if (!HKDF_extract(prk.peek_write(), &out_len, EVP_sha256(), master_key.key_material,
master_key.key_material_size, nullptr /* salt */, 0 /* salt_len */)) {
return TranslateLastOpenSslError();
}
KmErrorOr<Buffer> info =
BuildDerivationInfo(format, hw_enforced, sw_enforced, hidden, secure_deletion_data);
if (!info) return info.error();
if (!prk.advance_write(out_len) || !prk.available_read() || !info->available_read()) {
return KM_ERROR_UNKNOWN_ERROR;
}
Buffer keyEncryptionKey(kAes256KeyLength);
if (!HKDF_expand(keyEncryptionKey.peek_write(), keyEncryptionKey.available_write(), //
EVP_sha256(), //
prk.peek_read(), prk.available_read(), //
info->peek_read(), info->available_read())) {
return TranslateLastOpenSslError();
}
return keyEncryptionKey;
}
KmErrorOr<EncryptedKey> AesGcmEncryptKey(const AuthorizationSet& hw_enforced, //
const AuthorizationSet& sw_enforced, //
const AuthorizationSet& hidden, //
const SecureDeletionData& secure_deletion_data, //
const KeymasterKeyBlob& master_key, //
const KeymasterKeyBlob& plaintext, //
const AuthEncryptedBlobFormat format, //
Buffer nonce) {
KmErrorOr<Buffer> kek = DeriveAesGcmKeyEncryptionKey(format, hw_enforced, sw_enforced, hidden,
secure_deletion_data, master_key);
if (!kek) return kek.error();
bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
if (!ctx) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
int ciphertext_len = plaintext.size();
int unused_len = 0;
EncryptedKey retval;
retval.format = format;
retval.ciphertext = KeymasterKeyBlob(ciphertext_len);
retval.nonce = move(nonce);
retval.tag = Buffer(kAesGcmTagLength);
if (!(EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr /* engine */, kek->peek_read(),
retval.nonce.peek_read()) &&
EVP_EncryptUpdate(ctx.get(), retval.ciphertext.writable_data(), &ciphertext_len,
plaintext.key_material, plaintext.size()) &&
EVP_EncryptFinal_ex(ctx.get(), retval.ciphertext.writable_data() /* not written to */,
&unused_len) &&
EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kAesGcmTagLength,
retval.tag.peek_write()))) {
return TranslateLastOpenSslError();
}
if (plaintext.size() != static_cast<size_t>(ciphertext_len) || 0 != unused_len ||
!retval.tag.advance_write(kAesGcmTagLength)) {
return KM_ERROR_UNKNOWN_ERROR;
}
return retval;
}
KmErrorOr<KeymasterKeyBlob> AesGcmDecryptKey(const DeserializedKey& key,
const AuthorizationSet& hidden,
const SecureDeletionData& secure_deletion_data,
const KeymasterKeyBlob& master_key) {
KmErrorOr<Buffer> kek =
DeriveAesGcmKeyEncryptionKey(key.encrypted_key.format, key.hw_enforced, key.sw_enforced,
hidden, secure_deletion_data, master_key);
if (!kek) return kek.error();
bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
if (!ctx) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
int plaintext_len = key.encrypted_key.ciphertext.size();
int unused_len = 0;
KeymasterKeyBlob plaintext(plaintext_len);
if (!(EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr /* engine */, kek->peek_read(),
key.encrypted_key.nonce.peek_read()) &&
EVP_DecryptUpdate(ctx.get(), plaintext.writable_data(), &plaintext_len,
key.encrypted_key.ciphertext.key_material,
key.encrypted_key.ciphertext.size()) &&
EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kAesGcmTagLength,
const_cast<uint8_t*>(key.encrypted_key.tag.peek_read())))) {
return TranslateLastOpenSslError();
}
if (!EVP_DecryptFinal_ex(ctx.get(), plaintext.writable_data() /* not written to */,
&unused_len)) {
return KM_ERROR_INVALID_KEY_BLOB;
}
if (key.encrypted_key.ciphertext.size() != plaintext.size() || 0 != unused_len) {
return KM_ERROR_UNKNOWN_ERROR;
}
return plaintext;
}
} // namespace
KmErrorOr<KeymasterKeyBlob> SerializeAuthEncryptedBlob(const EncryptedKey& encrypted_key,
const AuthorizationSet& hw_enforced,
const AuthorizationSet& sw_enforced,
uint32_t key_slot) {
bool use_key_slot = requiresSecureDeletion(encrypted_key.format);
size_t size = 1 /* version byte */ + encrypted_key.nonce.SerializedSize() +
encrypted_key.ciphertext.SerializedSize() + encrypted_key.tag.SerializedSize() +
hw_enforced.SerializedSize() + sw_enforced.SerializedSize();
if (use_key_slot) size += sizeof(key_slot);
if (isVersionedFormat(encrypted_key.format)) {
size += sizeof(encrypted_key.kdf_version);
size += sizeof(encrypted_key.addl_info);
}
KeymasterKeyBlob retval;
if (!retval.Reset(size)) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
uint8_t* buf = retval.writable_data();
const uint8_t* end = retval.end();
*buf++ = encrypted_key.format;
buf = encrypted_key.nonce.Serialize(buf, end);
buf = encrypted_key.ciphertext.Serialize(buf, end);
buf = encrypted_key.tag.Serialize(buf, end);
if (isVersionedFormat(encrypted_key.format)) {
buf = append_uint32_to_buf(buf, end, encrypted_key.kdf_version);
buf = append_uint32_to_buf(buf, end, encrypted_key.addl_info);
}
buf = hw_enforced.Serialize(buf, end);
buf = sw_enforced.Serialize(buf, end);
if (use_key_slot) buf = append_uint32_to_buf(buf, end, key_slot);
if (buf != retval.end()) return KM_ERROR_UNKNOWN_ERROR;
return retval;
}
KmErrorOr<DeserializedKey> DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob) {
if (!key_blob.key_material || key_blob.key_material_size == 0) return KM_ERROR_INVALID_KEY_BLOB;
const uint8_t* tmp = key_blob.key_material;
const uint8_t** buf_ptr = &tmp;
const uint8_t* end = tmp + key_blob.key_material_size;
if (end <= *buf_ptr) return KM_ERROR_INVALID_KEY_BLOB;
DeserializedKey retval{};
retval.encrypted_key.format = static_cast<AuthEncryptedBlobFormat>(*(*buf_ptr)++);
if (!retval.encrypted_key.nonce.Deserialize(buf_ptr, end) || //
!retval.encrypted_key.ciphertext.Deserialize(buf_ptr, end) || //
!retval.encrypted_key.tag.Deserialize(buf_ptr, end)) {
return KM_ERROR_INVALID_KEY_BLOB;
}
if (isVersionedFormat(retval.encrypted_key.format)) {
if (!copy_uint32_from_buf(buf_ptr, end, &retval.encrypted_key.kdf_version) ||
!copy_uint32_from_buf(buf_ptr, end, &retval.encrypted_key.addl_info)) {
return KM_ERROR_INVALID_KEY_BLOB;
}
}
if (!retval.hw_enforced.Deserialize(buf_ptr, end) || //
!retval.sw_enforced.Deserialize(buf_ptr, end)) {
return KM_ERROR_INVALID_KEY_BLOB;
}
if (requiresSecureDeletion(retval.encrypted_key.format)) {
if (!copy_uint32_from_buf(buf_ptr, end, &retval.key_slot)) {
return KM_ERROR_INVALID_KEY_BLOB;
}
}
if (*buf_ptr != end) return KM_ERROR_INVALID_KEY_BLOB;
switch (retval.encrypted_key.format) {
case AES_OCB:
if (retval.encrypted_key.nonce.available_read() != OCB_NONCE_LENGTH ||
retval.encrypted_key.tag.available_read() != OCB_TAG_LENGTH) {
return KM_ERROR_INVALID_KEY_BLOB;
}
return retval;
case AES_GCM_WITH_SW_ENFORCED:
case AES_GCM_WITH_SECURE_DELETION:
case AES_GCM_WITH_SW_ENFORCED_VERSIONED:
case AES_GCM_WITH_SECURE_DELETION_VERSIONED:
if (retval.encrypted_key.nonce.available_read() != kAesGcmNonceLength ||
retval.encrypted_key.tag.available_read() != kAesGcmTagLength) {
return KM_ERROR_INVALID_KEY_BLOB;
}
return retval;
}
LOG_E("Invalid key blob format %d", retval.encrypted_key.format);
return KM_ERROR_INVALID_KEY_BLOB;
}
KmErrorOr<EncryptedKey>
EncryptKey(const KeymasterKeyBlob& plaintext, AuthEncryptedBlobFormat format,
const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced,
const AuthorizationSet& hidden, const SecureDeletionData& secure_deletion_data,
const KeymasterKeyBlob& master_key, const RandomSource& random) {
switch (format) {
case AES_OCB: {
EncryptedKey retval;
retval.format = format;
auto nonce = generate_nonce(random, OCB_NONCE_LENGTH);
if (!nonce) return nonce.error();
retval.nonce = std::move(*nonce);
retval.tag.Reinitialize(OCB_TAG_LENGTH);
keymaster_error_t error =
OcbEncryptKey(hw_enforced, sw_enforced, hidden, master_key, plaintext, retval.nonce,
&retval.ciphertext, &retval.tag);
if (error != KM_ERROR_OK) return error;
return retval;
}
case AES_GCM_WITH_SW_ENFORCED:
case AES_GCM_WITH_SECURE_DELETION:
case AES_GCM_WITH_SW_ENFORCED_VERSIONED:
case AES_GCM_WITH_SECURE_DELETION_VERSIONED: {
auto nonce = generate_nonce(random, kAesGcmNonceLength);
if (!nonce) return nonce.error();
return AesGcmEncryptKey(hw_enforced, sw_enforced, hidden, secure_deletion_data, master_key,
plaintext, format, std::move(*nonce));
}
}
LOG_E("Invalid key blob format %d", format);
return KM_ERROR_UNKNOWN_ERROR;
}
KmErrorOr<KeymasterKeyBlob> DecryptKey(const DeserializedKey& key, const AuthorizationSet& hidden,
const SecureDeletionData& secure_deletion_data,
const KeymasterKeyBlob& master_key) {
KeymasterKeyBlob retval;
switch (key.encrypted_key.format) {
case AES_OCB: {
keymaster_error_t error = OcbDecryptKey(
key.hw_enforced, key.sw_enforced, hidden, master_key, key.encrypted_key.ciphertext,
key.encrypted_key.nonce, key.encrypted_key.tag, &retval);
if (error != KM_ERROR_OK) return error;
return retval;
}
case AES_GCM_WITH_SW_ENFORCED:
case AES_GCM_WITH_SECURE_DELETION:
case AES_GCM_WITH_SW_ENFORCED_VERSIONED:
case AES_GCM_WITH_SECURE_DELETION_VERSIONED:
return AesGcmDecryptKey(key, hidden, secure_deletion_data, master_key);
}
LOG_E("Invalid key blob format %d", key.encrypted_key.format);
return KM_ERROR_INVALID_KEY_BLOB;
}
bool requiresSecureDeletion(const AuthEncryptedBlobFormat& fmt) {
return fmt == AES_GCM_WITH_SECURE_DELETION || fmt == AES_GCM_WITH_SECURE_DELETION_VERSIONED;
}
bool isVersionedFormat(const AuthEncryptedBlobFormat& fmt) {
return fmt == AES_GCM_WITH_SW_ENFORCED_VERSIONED ||
fmt == AES_GCM_WITH_SECURE_DELETION_VERSIONED;
}
} // namespace keymaster