| /* Copyright (c) 2014 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. | |
| * | |
| * Signature validation functions | |
| */ | |
| #include "2sysincludes.h" | |
| #include "2common.h" | |
| #include "2rsa.h" | |
| #include "2sha.h" | |
| #include "vb21_common.h" | |
| const char *vb21_common_desc(const void *buf) | |
| { | |
| const struct vb21_struct_common *c = buf; | |
| return c->desc_size ? (const char *)c + c->fixed_size : ""; | |
| } | |
| int vb21_verify_common_header(const void *parent, uint32_t parent_size) | |
| { | |
| const struct vb21_struct_common *c = parent; | |
| /* Parent buffer size must be at least the claimed total size */ | |
| if (parent_size < c->total_size) | |
| return VB2_ERROR_COMMON_TOTAL_SIZE; | |
| /* | |
| * And big enough for the fixed size, which itself must be at least as | |
| * big as the common struct header. | |
| */ | |
| if (c->total_size < c->fixed_size || c->fixed_size < sizeof(*c)) | |
| return VB2_ERROR_COMMON_FIXED_SIZE; | |
| /* Make sure sizes are all multiples of 32 bits */ | |
| if (!vb2_aligned(c->total_size, sizeof(uint32_t))) | |
| return VB2_ERROR_COMMON_TOTAL_UNALIGNED; | |
| if (!vb2_aligned(c->fixed_size, sizeof(uint32_t))) | |
| return VB2_ERROR_COMMON_FIXED_UNALIGNED; | |
| if (!vb2_aligned(c->desc_size, sizeof(uint32_t))) | |
| return VB2_ERROR_COMMON_DESC_UNALIGNED; | |
| /* Check description */ | |
| if (c->desc_size > 0) { | |
| /* Make sure description fits and doesn't wrap */ | |
| if (c->fixed_size + c->desc_size < c->fixed_size) | |
| return VB2_ERROR_COMMON_DESC_WRAPS; | |
| if (c->fixed_size + c->desc_size > c->total_size) | |
| return VB2_ERROR_COMMON_DESC_SIZE; | |
| /* Description must be null-terminated */ | |
| if (vb21_common_desc(c)[c->desc_size - 1] != 0) | |
| return VB2_ERROR_COMMON_DESC_TERMINATOR; | |
| } | |
| return VB2_SUCCESS; | |
| } | |
| int vb21_verify_common_member(const void *parent, | |
| uint32_t *min_offset, | |
| uint32_t member_offset, | |
| uint32_t member_size) | |
| { | |
| const struct vb21_struct_common *c = parent; | |
| uint32_t member_end = member_offset + member_size; | |
| /* Make sure member doesn't wrap */ | |
| if (member_end < member_offset) | |
| return VB2_ERROR_COMMON_MEMBER_WRAPS; | |
| /* Member offset and size must be 32-bit aligned */ | |
| if (!vb2_aligned(member_offset, sizeof(uint32_t)) || | |
| !vb2_aligned(member_size, sizeof(uint32_t))) | |
| return VB2_ERROR_COMMON_MEMBER_UNALIGNED; | |
| /* Initialize minimum offset if necessary */ | |
| if (!*min_offset) | |
| *min_offset = c->fixed_size + c->desc_size; | |
| /* Member must be after minimum offset */ | |
| if (member_offset < *min_offset) | |
| return VB2_ERROR_COMMON_MEMBER_OVERLAP; | |
| /* Member must end before total size */ | |
| if (member_end > c->total_size) | |
| return VB2_ERROR_COMMON_MEMBER_SIZE; | |
| /* Update minimum offset for subsequent checks */ | |
| *min_offset = member_end; | |
| return VB2_SUCCESS; | |
| } | |
| int vb21_verify_common_subobject(const void *parent, | |
| uint32_t *min_offset, | |
| uint32_t member_offset) | |
| { | |
| const struct vb21_struct_common *p = parent; | |
| const struct vb21_struct_common *m = | |
| (const struct vb21_struct_common *) | |
| ((const uint8_t *)parent + member_offset); | |
| int rv; | |
| /* | |
| * Verify the parent has space at the member offset for the common | |
| * header. | |
| */ | |
| rv = vb21_verify_common_member(parent, min_offset, member_offset, | |
| sizeof(*m)); | |
| if (rv) | |
| return rv; | |
| /* | |
| * Now it's safe to look at the member's header, and verify any | |
| * additional data for the object past its common header fits in the | |
| * parent. | |
| */ | |
| rv = vb21_verify_common_header(m, p->total_size - member_offset); | |
| if (rv) | |
| return rv; | |
| /* Advance the min offset to the end of the subobject */ | |
| *min_offset = member_offset + m->total_size; | |
| return VB2_SUCCESS; | |
| } | |
| uint32_t vb2_sig_size(enum vb2_signature_algorithm sig_alg, | |
| enum vb2_hash_algorithm hash_alg) | |
| { | |
| uint32_t digest_size = vb2_digest_size(hash_alg); | |
| /* Fail if we don't support the hash algorithm */ | |
| if (!digest_size) | |
| return 0; | |
| /* Handle unsigned hashes */ | |
| if (sig_alg == VB2_SIG_NONE) | |
| return digest_size; | |
| return vb2_rsa_sig_size(sig_alg); | |
| } | |
| const struct vb2_id *vb2_hash_id(enum vb2_hash_algorithm hash_alg) | |
| { | |
| switch(hash_alg) { | |
| #ifdef VB2_SUPPORT_SHA1 | |
| case VB2_HASH_SHA1: | |
| { | |
| static const struct vb2_id id = VB2_ID_NONE_SHA1; | |
| return &id; | |
| } | |
| #endif | |
| #ifdef VB2_SUPPORT_SHA256 | |
| case VB2_HASH_SHA256: | |
| { | |
| static const struct vb2_id id = VB2_ID_NONE_SHA256; | |
| return &id; | |
| } | |
| #endif | |
| #ifdef VB2_SUPPORT_SHA512 | |
| case VB2_HASH_SHA512: | |
| { | |
| static const struct vb2_id id = VB2_ID_NONE_SHA512; | |
| return &id; | |
| } | |
| #endif | |
| default: | |
| return NULL; | |
| } | |
| } | |
| int vb21_verify_signature(const struct vb21_signature *sig, uint32_t size) | |
| { | |
| uint32_t min_offset = 0; | |
| uint32_t expect_sig_size; | |
| int rv; | |
| /* Check magic number */ | |
| if (sig->c.magic != VB21_MAGIC_SIGNATURE) | |
| return VB2_ERROR_SIG_MAGIC; | |
| /* Make sure common header is good */ | |
| rv = vb21_verify_common_header(sig, size); | |
| if (rv) | |
| return rv; | |
| /* | |
| * Check for compatible version. No need to check minor version, since | |
| * that's compatible across readers matching the major version, and we | |
| * haven't added any new fields. | |
| */ | |
| if (sig->c.struct_version_major != VB21_SIGNATURE_VERSION_MAJOR) | |
| return VB2_ERROR_SIG_VERSION; | |
| /* Make sure header is big enough for signature */ | |
| if (sig->c.fixed_size < sizeof(*sig)) | |
| return VB2_ERROR_SIG_HEADER_SIZE; | |
| /* Make sure signature data is inside */ | |
| rv = vb21_verify_common_member(sig, &min_offset, | |
| sig->sig_offset, sig->sig_size); | |
| if (rv) | |
| return rv; | |
| /* Make sure signature size is correct for the algorithm */ | |
| expect_sig_size = vb2_sig_size(sig->sig_alg, sig->hash_alg); | |
| if (!expect_sig_size) | |
| return VB2_ERROR_SIG_ALGORITHM; | |
| if (sig->sig_size != expect_sig_size) | |
| return VB2_ERROR_SIG_SIZE; | |
| return VB2_SUCCESS; | |
| } | |
| /** | |
| * Return the signature data for a signature | |
| */ | |
| static uint8_t *vb21_signature_data(struct vb21_signature *sig) | |
| { | |
| return (uint8_t *)sig + sig->sig_offset; | |
| } | |
| int vb21_verify_digest(const struct vb2_public_key *key, | |
| struct vb21_signature *sig, | |
| const uint8_t *digest, | |
| const struct vb2_workbuf *wb) | |
| { | |
| uint32_t key_sig_size = vb2_sig_size(key->sig_alg, key->hash_alg); | |
| /* If we can't figure out the signature size, key algorithm was bad */ | |
| if (!key_sig_size) | |
| return VB2_ERROR_VDATA_ALGORITHM; | |
| /* Make sure the signature and key algorithms match */ | |
| if (key->sig_alg != sig->sig_alg || key->hash_alg != sig->hash_alg) | |
| return VB2_ERROR_VDATA_ALGORITHM_MISMATCH; | |
| if (sig->sig_size != key_sig_size) | |
| return VB2_ERROR_VDATA_SIG_SIZE; | |
| if (key->sig_alg == VB2_SIG_NONE) { | |
| /* Bare hash */ | |
| if (vb2_safe_memcmp(vb21_signature_data(sig), | |
| digest, key_sig_size)) | |
| return VB2_ERROR_VDATA_VERIFY_DIGEST; | |
| return VB2_SUCCESS; | |
| } else { | |
| /* RSA-signed digest */ | |
| return vb2_rsa_verify_digest(key, | |
| vb21_signature_data(sig), | |
| digest, wb); | |
| } | |
| } | |
| int vb21_verify_data(const void *data, | |
| uint32_t size, | |
| struct vb21_signature *sig, | |
| const struct vb2_public_key *key, | |
| const struct vb2_workbuf *wb) | |
| { | |
| struct vb2_workbuf wblocal = *wb; | |
| struct vb2_digest_context *dc; | |
| uint8_t *digest; | |
| uint32_t digest_size; | |
| int rv; | |
| if (sig->data_size != size) { | |
| VB2_DEBUG("Wrong amount of data signed.\n"); | |
| return VB2_ERROR_VDATA_SIZE; | |
| } | |
| /* Digest goes at start of work buffer */ | |
| digest_size = vb2_digest_size(key->hash_alg); | |
| if (!digest_size) | |
| return VB2_ERROR_VDATA_DIGEST_SIZE; | |
| digest = vb2_workbuf_alloc(&wblocal, digest_size); | |
| if (!digest) | |
| return VB2_ERROR_VDATA_WORKBUF_DIGEST; | |
| /* Hashing requires temp space for the context */ | |
| dc = vb2_workbuf_alloc(&wblocal, sizeof(*dc)); | |
| if (!dc) | |
| return VB2_ERROR_VDATA_WORKBUF_HASHING; | |
| rv = vb2_digest_init(dc, key->hash_alg); | |
| if (rv) | |
| return rv; | |
| rv = vb2_digest_extend(dc, data, size); | |
| if (rv) | |
| return rv; | |
| rv = vb2_digest_finalize(dc, digest, digest_size); | |
| if (rv) | |
| return rv; | |
| vb2_workbuf_free(&wblocal, sizeof(*dc)); | |
| return vb21_verify_digest(key, sig, digest, &wblocal); | |
| } | |
| int vb21_verify_keyblock(struct vb21_keyblock *block, | |
| uint32_t size, | |
| const struct vb2_public_key *key, | |
| const struct vb2_workbuf *wb) | |
| { | |
| uint32_t min_offset = 0, sig_offset; | |
| int rv, i; | |
| /* Check magic number */ | |
| if (block->c.magic != VB21_MAGIC_KEYBLOCK) | |
| return VB2_ERROR_KEYBLOCK_MAGIC; | |
| /* Make sure common header is good */ | |
| rv = vb21_verify_common_header(block, size); | |
| if (rv) | |
| return rv; | |
| /* | |
| * Check for compatible version. No need to check minor version, since | |
| * that's compatible across readers matching the major version, and we | |
| * haven't added any new fields. | |
| */ | |
| if (block->c.struct_version_major != VB21_KEYBLOCK_VERSION_MAJOR) | |
| return VB2_ERROR_KEYBLOCK_HEADER_VERSION; | |
| /* Make sure header is big enough */ | |
| if (block->c.fixed_size < sizeof(*block)) | |
| return VB2_ERROR_KEYBLOCK_SIZE; | |
| /* Make sure data key is inside */ | |
| rv = vb21_verify_common_subobject(block, &min_offset, | |
| block->key_offset); | |
| if (rv) | |
| return rv; | |
| /* Loop over signatures */ | |
| sig_offset = block->sig_offset; | |
| for (i = 0; i < block->sig_count; i++, sig_offset = min_offset) { | |
| struct vb21_signature *sig; | |
| /* Make sure signature is inside keyblock */ | |
| rv = vb21_verify_common_subobject(block, &min_offset, | |
| sig_offset); | |
| if (rv) | |
| return rv; | |
| sig = (struct vb21_signature *)((uint8_t *)block + sig_offset); | |
| /* Verify the signature integrity */ | |
| rv = vb21_verify_signature(sig, | |
| block->c.total_size - sig_offset); | |
| if (rv) | |
| return rv; | |
| /* Skip signature if it doesn't match the key ID */ | |
| if (memcmp(&sig->id, key->id, VB2_ID_NUM_BYTES)) | |
| continue; | |
| /* Make sure we signed the right amount of data */ | |
| if (sig->data_size != block->sig_offset) | |
| return VB2_ERROR_KEYBLOCK_SIGNED_SIZE; | |
| return vb21_verify_data(block, block->sig_offset, sig, key, wb); | |
| } | |
| /* If we're still here, no signature matched the key ID */ | |
| return VB2_ERROR_KEYBLOCK_SIG_ID; | |
| } | |
| int vb21_verify_fw_preamble(struct vb21_fw_preamble *preamble, | |
| uint32_t size, | |
| const struct vb2_public_key *key, | |
| const struct vb2_workbuf *wb) | |
| { | |
| struct vb21_signature *sig; | |
| uint32_t min_offset = 0, hash_offset; | |
| int rv, i; | |
| /* Check magic number */ | |
| if (preamble->c.magic != VB21_MAGIC_FW_PREAMBLE) | |
| return VB2_ERROR_PREAMBLE_MAGIC; | |
| /* Make sure common header is good */ | |
| rv = vb21_verify_common_header(preamble, size); | |
| if (rv) | |
| return rv; | |
| /* | |
| * Check for compatible version. No need to check minor version, since | |
| * that's compatible across readers matching the major version, and we | |
| * haven't added any new fields. | |
| */ | |
| if (preamble->c.struct_version_major != VB21_FW_PREAMBLE_VERSION_MAJOR) | |
| return VB2_ERROR_PREAMBLE_HEADER_VERSION; | |
| /* Make sure header is big enough */ | |
| if (preamble->c.fixed_size < sizeof(*preamble)) | |
| return VB2_ERROR_PREAMBLE_SIZE; | |
| /* Make sure all hash signatures are inside */ | |
| hash_offset = preamble->hash_offset; | |
| for (i = 0; i < preamble->hash_count; i++, hash_offset = min_offset) { | |
| /* Make sure signature is inside preamble */ | |
| rv = vb21_verify_common_subobject(preamble, &min_offset, | |
| hash_offset); | |
| if (rv) | |
| return rv; | |
| sig = (struct vb21_signature *) | |
| ((uint8_t *)preamble + hash_offset); | |
| /* Verify the signature integrity */ | |
| rv = vb21_verify_signature( | |
| sig, preamble->c.total_size - hash_offset); | |
| if (rv) | |
| return rv; | |
| /* Hashes must all be unsigned */ | |
| if (sig->sig_alg != VB2_SIG_NONE) | |
| return VB2_ERROR_PREAMBLE_HASH_SIGNED; | |
| } | |
| /* Make sure signature is inside preamble */ | |
| rv = vb21_verify_common_subobject(preamble, &min_offset, | |
| preamble->sig_offset); | |
| if (rv) | |
| return rv; | |
| /* Verify preamble signature */ | |
| sig = (struct vb21_signature *)((uint8_t *)preamble + | |
| preamble->sig_offset); | |
| rv = vb21_verify_data(preamble, preamble->sig_offset, sig, key, wb); | |
| if (rv) | |
| return rv; | |
| return VB2_SUCCESS; | |
| } |