| /* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Common functions between firmware and kernel verified boot. |
| * (Firmware portion) |
| */ |
| |
| |
| #include "vboot_api.h" |
| #include "vboot_common.h" |
| #include "utility.h" |
| |
| |
| char* kVbootErrors[VBOOT_ERROR_MAX] = { |
| "Success.", |
| "Key block invalid.", |
| "Key block signature failed.", |
| "Key block hash failed.", |
| "Public key invalid.", |
| "Preamble invalid.", |
| "Preamble signature check failed.", |
| "Shared data invalid." |
| }; |
| |
| |
| uint64_t OffsetOf(const void *base, const void *ptr) { |
| return (uint64_t)(size_t)ptr - (uint64_t)(size_t)base; |
| } |
| |
| |
| /* Helper functions to get data pointed to by a public key or signature. */ |
| uint8_t* GetPublicKeyData(VbPublicKey* key) { |
| return (uint8_t*)key + key->key_offset; |
| } |
| |
| const uint8_t* GetPublicKeyDataC(const VbPublicKey* key) { |
| return (const uint8_t*)key + key->key_offset; |
| } |
| |
| uint8_t* GetSignatureData(VbSignature* sig) { |
| return (uint8_t*)sig + sig->sig_offset; |
| } |
| |
| const uint8_t* GetSignatureDataC(const VbSignature* sig) { |
| return (const uint8_t*)sig + sig->sig_offset; |
| } |
| |
| |
| /* Helper functions to verify the data pointed to by a subfield is inside |
| * the parent data. Returns 0 if inside, 1 if error. */ |
| int VerifyMemberInside(const void* parent, uint64_t parent_size, |
| const void* member, uint64_t member_size, |
| uint64_t member_data_offset, |
| uint64_t member_data_size) { |
| uint64_t end = OffsetOf(parent, member); |
| |
| if (end > parent_size) |
| return 1; |
| |
| if (UINT64_MAX - end < member_size) |
| return 1; /* Detect wraparound in integer math */ |
| if (end + member_size > parent_size) |
| return 1; |
| |
| if (UINT64_MAX - end < member_data_offset) |
| return 1; |
| end += member_data_offset; |
| if (end > parent_size) |
| return 1; |
| |
| if (UINT64_MAX - end < member_data_size) |
| return 1; |
| if (end + member_data_size > parent_size) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| int VerifyPublicKeyInside(const void* parent, uint64_t parent_size, |
| const VbPublicKey* key) { |
| return VerifyMemberInside(parent, parent_size, |
| key, sizeof(VbPublicKey), |
| key->key_offset, key->key_size); |
| } |
| |
| |
| int VerifySignatureInside(const void* parent, uint64_t parent_size, |
| const VbSignature* sig) { |
| return VerifyMemberInside(parent, parent_size, |
| sig, sizeof(VbSignature), |
| sig->sig_offset, sig->sig_size); |
| } |
| |
| |
| void PublicKeyInit(VbPublicKey* key, uint8_t* key_data, uint64_t key_size) { |
| key->key_offset = OffsetOf(key, key_data); |
| key->key_size = key_size; |
| key->algorithm = kNumAlgorithms; /* Key not present yet */ |
| key->key_version = 0; |
| } |
| |
| |
| int PublicKeyCopy(VbPublicKey* dest, const VbPublicKey* src) { |
| if (dest->key_size < src->key_size) |
| return 1; |
| |
| dest->key_size = src->key_size; |
| dest->algorithm = src->algorithm; |
| dest->key_version = src->key_version; |
| Memcpy(GetPublicKeyData(dest), GetPublicKeyDataC(src), src->key_size); |
| return 0; |
| } |
| |
| |
| RSAPublicKey* PublicKeyToRSA(const VbPublicKey* key) { |
| RSAPublicKey *rsa; |
| uint64_t key_size; |
| |
| if (kNumAlgorithms <= key->algorithm) { |
| VBDEBUG(("Invalid algorithm.\n")); |
| return NULL; |
| } |
| if (!RSAProcessedKeySize(key->algorithm, &key_size) || |
| key_size != key->key_size) { |
| VBDEBUG(("Wrong key size for algorithm\n")); |
| return NULL; |
| } |
| |
| rsa = RSAPublicKeyFromBuf(GetPublicKeyDataC(key), key->key_size); |
| if (!rsa) |
| return NULL; |
| |
| rsa->algorithm = (unsigned int)key->algorithm; |
| return rsa; |
| } |
| |
| |
| int VerifyData(const uint8_t* data, uint64_t size, const VbSignature *sig, |
| const RSAPublicKey* key) { |
| |
| if (sig->sig_size != siglen_map[key->algorithm]) { |
| VBDEBUG(("Wrong signature size for algorithm.\n")); |
| return 1; |
| } |
| if (sig->data_size > size) { |
| VBDEBUG(("Data buffer smaller than length of signed data.\n")); |
| return 1; |
| } |
| |
| if (!RSAVerifyBinary_f(NULL, key, data, sig->data_size, |
| GetSignatureDataC(sig), key->algorithm)) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| int VerifyDigest(const uint8_t* digest, const VbSignature *sig, |
| const RSAPublicKey* key) { |
| |
| if (sig->sig_size != siglen_map[key->algorithm]) { |
| VBDEBUG(("Wrong signature size for algorithm.\n")); |
| return 1; |
| } |
| |
| if (!RSAVerifyBinaryWithDigest_f(NULL, key, digest, |
| GetSignatureDataC(sig), key->algorithm)) |
| return 1; |
| |
| return 0; |
| } |
| |
| |
| int EqualData(const uint8_t* data, uint64_t size, const VbSignature *hash, |
| const RSAPublicKey* key) { |
| uint8_t* digest = NULL; |
| int rv; |
| |
| if (hash->sig_size != hash_size_map[key->algorithm]) { |
| VBDEBUG(("Wrong hash size for algorithm.\n")); |
| return 1; |
| } |
| if (hash->data_size > size) { |
| VBDEBUG(("Data buffer smaller than length of signed data.\n")); |
| return 1; |
| } |
| |
| digest = DigestBuf(data, hash->data_size, key->algorithm); |
| |
| rv = SafeMemcmp(digest, GetSignatureDataC(hash), hash->sig_size); |
| VbExFree(digest); |
| return rv; |
| } |
| |
| |
| int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size, |
| const VbPublicKey *key, int hash_only) { |
| |
| const VbSignature* sig; |
| |
| /* Sanity checks before attempting signature of data */ |
| if(size < sizeof(VbKeyBlockHeader)) { |
| VBDEBUG(("Not enough space for key block header.\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (SafeMemcmp(block->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE)) { |
| VBDEBUG(("Not a valid verified boot key block.\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (block->header_version_major != KEY_BLOCK_HEADER_VERSION_MAJOR) { |
| VBDEBUG(("Incompatible key block header version.\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (size < block->key_block_size) { |
| VBDEBUG(("Not enough data for key block.\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (!hash_only && !key) { |
| VBDEBUG(("Missing required public key.\n")); |
| return VBOOT_PUBLIC_KEY_INVALID; |
| } |
| |
| /* Check signature or hash, depending on the hash_only parameter. Note that |
| * we don't require a key even if the keyblock has a signature, because the |
| * caller may not care if the keyblock itself is signed (for example, booting |
| * a Google-signed kernel in developer mode). |
| */ |
| if (hash_only) { |
| /* Check hash */ |
| uint8_t* header_checksum = NULL; |
| int rv; |
| |
| sig = &block->key_block_checksum; |
| |
| if (VerifySignatureInside(block, block->key_block_size, sig)) { |
| VBDEBUG(("Key block hash off end of block\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (sig->sig_size != SHA512_DIGEST_SIZE) { |
| VBDEBUG(("Wrong hash size for key block.\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| /* Make sure advertised signature data sizes are sane. */ |
| if (block->key_block_size < sig->data_size) { |
| VBDEBUG(("Signature calculated past end of the block\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| VBDEBUG(("Checking key block hash only...\n")); |
| header_checksum = DigestBuf((const uint8_t*)block, sig->data_size, |
| SHA512_DIGEST_ALGORITHM); |
| rv = SafeMemcmp(header_checksum, GetSignatureDataC(sig), |
| SHA512_DIGEST_SIZE); |
| VbExFree(header_checksum); |
| if (rv) { |
| VBDEBUG(("Invalid key block hash.\n")); |
| return VBOOT_KEY_BLOCK_HASH; |
| } |
| } else { |
| /* Check signature */ |
| RSAPublicKey* rsa; |
| int rv; |
| |
| sig = &block->key_block_signature; |
| |
| if (VerifySignatureInside(block, block->key_block_size, sig)) { |
| VBDEBUG(("Key block signature off end of block\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| rsa = PublicKeyToRSA(key); |
| if (!rsa) { |
| VBDEBUG(("Invalid public key\n")); |
| return VBOOT_PUBLIC_KEY_INVALID; |
| } |
| |
| /* Make sure advertised signature data sizes are sane. */ |
| if (block->key_block_size < sig->data_size) { |
| VBDEBUG(("Signature calculated past end of the block\n")); |
| RSAPublicKeyFree(rsa); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| VBDEBUG(("Checking key block signature...\n")); |
| rv = VerifyData((const uint8_t*)block, size, sig, rsa); |
| RSAPublicKeyFree(rsa); |
| if (rv) { |
| VBDEBUG(("Invalid key block signature.\n")); |
| return VBOOT_KEY_BLOCK_SIGNATURE; |
| } |
| } |
| |
| /* Verify we signed enough data */ |
| if (sig->data_size < sizeof(VbKeyBlockHeader)) { |
| VBDEBUG(("Didn't sign enough data\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| /* Verify data key is inside the block and inside signed data */ |
| if (VerifyPublicKeyInside(block, block->key_block_size, &block->data_key)) { |
| VBDEBUG(("Data key off end of key block\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| if (VerifyPublicKeyInside(block, sig->data_size, &block->data_key)) { |
| VBDEBUG(("Data key off end of signed data\n")); |
| return VBOOT_KEY_BLOCK_INVALID; |
| } |
| |
| /* Success */ |
| return VBOOT_SUCCESS; |
| } |
| |
| |
| int VerifyECPreamble(const VbECPreambleHeader* preamble, |
| uint64_t size, const RSAPublicKey* key) { |
| |
| const VbSignature* sig = &preamble->preamble_signature; |
| |
| /* Sanity checks before attempting signature of data */ |
| if(size < EXPECTED_VB_EC_PREAMBLE_HEADER1_0_SIZE) { |
| VBDEBUG(("Not enough data for EC preamble header.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (preamble->header_version_major != |
| EC_PREAMBLE_HEADER_VERSION_MAJOR) { |
| VBDEBUG(("Incompatible EC preamble header version (%d, not %d).\n", |
| preamble->header_version_major, |
| EC_PREAMBLE_HEADER_VERSION_MAJOR)); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (size < preamble->preamble_size) { |
| VBDEBUG(("Not enough data for EC preamble.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Check signature */ |
| if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) { |
| VBDEBUG(("EC preamble signature off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Make sure advertised signature data sizes are sane. */ |
| if (preamble->preamble_size < sig->data_size) { |
| VBDEBUG(("EC signature calculated past end of the block\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| if (VerifyData((const uint8_t*)preamble, size, sig, key)) { |
| VBDEBUG(("EC preamble signature validation failed\n")); |
| return VBOOT_PREAMBLE_SIGNATURE; |
| } |
| |
| /* Verify we signed enough data */ |
| if (sig->data_size < sizeof(VbFirmwarePreambleHeader)) { |
| VBDEBUG(("Didn't sign enough data\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Verify body digest is inside the signed data */ |
| if (VerifySignatureInside(preamble, sig->data_size, |
| &preamble->body_digest)) { |
| VBDEBUG(("EC body digest off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Success */ |
| return VBOOT_SUCCESS; |
| } |
| |
| int VerifyFirmwarePreamble(const VbFirmwarePreambleHeader* preamble, |
| uint64_t size, const RSAPublicKey* key) { |
| |
| const VbSignature* sig = &preamble->preamble_signature; |
| |
| /* Sanity checks before attempting signature of data */ |
| if(size < EXPECTED_VBFIRMWAREPREAMBLEHEADER2_0_SIZE) { |
| VBDEBUG(("Not enough data for preamble header 2.0.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (preamble->header_version_major != |
| FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR) { |
| VBDEBUG(("Incompatible firmware preamble header version.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (size < preamble->preamble_size) { |
| VBDEBUG(("Not enough data for preamble.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Check signature */ |
| if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) { |
| VBDEBUG(("Preamble signature off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Make sure advertised signature data sizes are sane. */ |
| if (preamble->preamble_size < sig->data_size) { |
| VBDEBUG(("Signature calculated past end of the block\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| if (VerifyData((const uint8_t*)preamble, size, sig, key)) { |
| VBDEBUG(("Preamble signature validation failed\n")); |
| return VBOOT_PREAMBLE_SIGNATURE; |
| } |
| |
| /* Verify we signed enough data */ |
| if (sig->data_size < sizeof(VbFirmwarePreambleHeader)) { |
| VBDEBUG(("Didn't sign enough data\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Verify body signature is inside the signed data */ |
| if (VerifySignatureInside(preamble, sig->data_size, |
| &preamble->body_signature)) { |
| VBDEBUG(("Firmware body signature off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Verify kernel subkey is inside the signed data */ |
| if (VerifyPublicKeyInside(preamble, sig->data_size, |
| &preamble->kernel_subkey)) { |
| VBDEBUG(("Kernel subkey off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* If the preamble header version is at least 2.1, verify we have |
| * space for the added fields from 2.1. */ |
| if (preamble->header_version_minor >= 1) { |
| if(size < EXPECTED_VBFIRMWAREPREAMBLEHEADER2_1_SIZE) { |
| VBDEBUG(("Not enough data for preamble header 2.1.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| } |
| |
| /* Success */ |
| return VBOOT_SUCCESS; |
| } |
| |
| |
| uint32_t VbGetFirmwarePreambleFlags(const VbFirmwarePreambleHeader* preamble) { |
| if (preamble->header_version_minor < 1) { |
| /* Old structure; return default flags. (Note that we don't need |
| * to check header_version_major; if that's not 2 then |
| * VerifyFirmwarePreamble() would have already failed. */ |
| return 0; |
| } |
| |
| return preamble->flags; |
| } |
| |
| |
| int VerifyKernelPreamble(const VbKernelPreambleHeader* preamble, |
| uint64_t size, const RSAPublicKey* key) { |
| |
| const VbSignature* sig = &preamble->preamble_signature; |
| |
| /* Sanity checks before attempting signature of data */ |
| if(size < sizeof(VbKernelPreambleHeader)) { |
| VBDEBUG(("Not enough data for preamble header.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (preamble->header_version_major != KERNEL_PREAMBLE_HEADER_VERSION_MAJOR) { |
| VBDEBUG(("Incompatible kernel preamble header version.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (size < preamble->preamble_size) { |
| VBDEBUG(("Not enough data for preamble.\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Check signature */ |
| if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) { |
| VBDEBUG(("Preamble signature off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| if (VerifyData((const uint8_t*)preamble, size, sig, key)) { |
| VBDEBUG(("Preamble signature validation failed\n")); |
| return VBOOT_PREAMBLE_SIGNATURE; |
| } |
| |
| /* Verify we signed enough data */ |
| if (sig->data_size < sizeof(VbKernelPreambleHeader)) { |
| VBDEBUG(("Didn't sign enough data\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Verify body signature is inside the signed data */ |
| if (VerifySignatureInside(preamble, sig->data_size, |
| &preamble->body_signature)) { |
| VBDEBUG(("Kernel body signature off end of preamble\n")); |
| return VBOOT_PREAMBLE_INVALID; |
| } |
| |
| /* Success */ |
| return VBOOT_SUCCESS; |
| } |
| |
| |
| int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size) { |
| |
| VBDEBUG(("VbSharedDataInit, %d bytes, header %d bytes\n", (int)size, |
| sizeof(VbSharedDataHeader))); |
| |
| if (size < sizeof(VbSharedDataHeader)) { |
| VBDEBUG(("Not enough data for header.\n")); |
| return VBOOT_SHARED_DATA_INVALID; |
| } |
| if (size < VB_SHARED_DATA_MIN_SIZE) { |
| VBDEBUG(("Shared data buffer too small.\n")); |
| return VBOOT_SHARED_DATA_INVALID; |
| } |
| |
| if (!header) |
| return VBOOT_SHARED_DATA_INVALID; |
| |
| /* Zero the header */ |
| Memset(header, 0, sizeof(VbSharedDataHeader)); |
| |
| /* Initialize fields */ |
| header->magic = VB_SHARED_DATA_MAGIC; |
| header->struct_version = VB_SHARED_DATA_VERSION; |
| header->struct_size = sizeof(VbSharedDataHeader); |
| header->data_size = size; |
| header->data_used = sizeof(VbSharedDataHeader); |
| header->firmware_index = 0xFF; |
| |
| /* Success */ |
| return VBOOT_SUCCESS; |
| } |
| |
| |
| uint64_t VbSharedDataReserve(VbSharedDataHeader* header, uint64_t size) { |
| uint64_t offs = header->data_used; |
| |
| VBDEBUG(("VbSharedDataReserve %d bytes at %d\n", (int)size, (int)offs)); |
| |
| if (!header || size > header->data_size - header->data_used) { |
| VBDEBUG(("VbSharedData buffer out of space.\n")); |
| return 0; /* Not initialized, or not enough space left. */ |
| } |
| header->data_used += size; |
| return offs; |
| } |
| |
| |
| int VbSharedDataSetKernelKey(VbSharedDataHeader* header, |
| const VbPublicKey* src) { |
| |
| VbPublicKey *kdest = &header->kernel_subkey; |
| |
| if (!header) |
| return VBOOT_SHARED_DATA_INVALID; |
| |
| /* Attempt to allocate space for the key, if it hasn't been allocated yet */ |
| if (!header->kernel_subkey_data_offset) { |
| header->kernel_subkey_data_offset = VbSharedDataReserve(header, |
| src->key_size); |
| if (!header->kernel_subkey_data_offset) |
| return VBOOT_SHARED_DATA_INVALID; |
| header->kernel_subkey_data_size = src->key_size; |
| } |
| |
| /* Copy the kernel sign key blob into the destination buffer */ |
| PublicKeyInit(kdest, (uint8_t*)header + header->kernel_subkey_data_offset, |
| header->kernel_subkey_data_size); |
| |
| return PublicKeyCopy(kdest, src); |
| } |