/* 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. * * Common functions between firmware and kernel verified boot. * (Firmware portion) */ #include "2sysincludes.h" #include "2common.h" #include "2rsa.h" #include "2sha.h" int vb2_align(uint8_t **ptr, uint32_t *size, uint32_t align, uint32_t want_size) { uintptr_t p = (uintptr_t)*ptr; uintptr_t offs = p & (align - 1); if (offs) { offs = align - offs; if (*size < offs) return VB2_ERROR_BUFFER_TOO_SMALL; *ptr += offs; *size -= offs; } if (*size < want_size) return VB2_ERROR_BUFFER_TOO_SMALL; return VB2_SUCCESS; } void vb2_workbuf_init(struct vb2_workbuf *wb, uint8_t *buf, uint32_t size) { wb->buf = buf; wb->size = size; /* Align the buffer so allocations will be aligned */ if (vb2_align(&wb->buf, &wb->size, VB2_WORKBUF_ALIGN, 0)) wb->size = 0; } /** * Round up a number to a multiple of VB2_WORKBUF_ALIGN * * @param v Number to round up * @return The number, rounded up. */ static __inline uint32_t wb_round_up(uint32_t v) { return (v + VB2_WORKBUF_ALIGN - 1) & ~(VB2_WORKBUF_ALIGN - 1); } void *vb2_workbuf_alloc(struct vb2_workbuf *wb, uint32_t size) { uint8_t *ptr = wb->buf; /* Round up size to work buffer alignment */ size = wb_round_up(size); if (size > wb->size) return NULL; wb->buf += size; wb->size -= size; return ptr; } void *vb2_workbuf_realloc(struct vb2_workbuf *wb, uint32_t oldsize, uint32_t newsize) { /* * Just free and allocate to update the size. No need to move/copy * memory, since the new pointer is guaranteed to be the same as the * old one. The new allocation can fail, if the new size is too big. */ vb2_workbuf_free(wb, oldsize); return vb2_workbuf_alloc(wb, newsize); } void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size) { /* Round up size to work buffer alignment */ size = wb_round_up(size); wb->buf -= size; wb->size += size; } const uint8_t *vb2_packed_key_data(const struct vb2_packed_key *key) { return (const uint8_t *)key + key->key_offset; } uint8_t *vb2_signature_data(struct vb2_signature *sig) { return (uint8_t *)sig + sig->sig_offset; } ptrdiff_t vb2_offset_of(const void *base, const void *ptr) { return (uintptr_t)ptr - (uintptr_t)base; } int vb2_verify_member_inside(const void *parent, uint32_t parent_size, const void *member, uint32_t member_size, uint32_t member_data_offset, uint32_t member_data_size) { const size_t psize = (size_t)parent_size; const uintptr_t parent_end = (uintptr_t)parent + parent_size; const ptrdiff_t member_offs = vb2_offset_of(parent, member); const ptrdiff_t member_end_offs = member_offs + member_size; const ptrdiff_t data_offs = member_offs + member_data_offset; const ptrdiff_t data_end_offs = data_offs + member_data_size; /* Make sure parent doesn't wrap */ if (parent_end < (uintptr_t)parent) return VB2_ERROR_UNKNOWN; /* * Make sure the member is fully contained in the parent and doesn't * wrap. Use >, not >=, since member_size = 0 is possible. */ if (member_end_offs < member_offs) return VB2_ERROR_UNKNOWN; if (member_offs > psize || member_end_offs > psize) return VB2_ERROR_UNKNOWN; /* Make sure parent fully contains member data */ if (data_end_offs < data_offs) return VB2_ERROR_UNKNOWN; if (data_offs > psize || data_end_offs > psize) return VB2_ERROR_UNKNOWN; return VB2_SUCCESS; } int vb2_verify_signature_inside(const void *parent, uint32_t parent_size, const struct vb2_signature *sig) { return vb2_verify_member_inside(parent, parent_size, sig, sizeof(*sig), sig->sig_offset, sig->sig_size); } int vb2_verify_packed_key_inside(const void *parent, uint32_t parent_size, const struct vb2_packed_key *key) { return vb2_verify_member_inside(parent, parent_size, key, sizeof(*key), key->key_offset, key->key_size); } int vb2_unpack_key(struct vb2_public_key *key, const uint8_t *buf, uint32_t size) { const struct vb2_packed_key *packed_key = (const struct vb2_packed_key *)buf; const uint32_t *buf32; uint32_t expected_key_size; int rv; /* Make sure passed buffer is big enough for the packed key */ rv = vb2_verify_packed_key_inside(buf, size, packed_key); if (rv) return rv; if (packed_key->algorithm >= VB2_ALG_COUNT) { VB2_DEBUG("Invalid algorithm.\n"); return VB2_ERROR_BAD_ALGORITHM; } expected_key_size = vb2_packed_key_size(packed_key->algorithm); if (!expected_key_size || expected_key_size != packed_key->key_size) { VB2_DEBUG("Wrong key size for algorithm\n"); return VB2_ERROR_BAD_KEY; } /* Make sure source buffer is 32-bit aligned */ buf32 = (const uint32_t *)vb2_packed_key_data(packed_key); if (!vb_aligned(buf32, sizeof(uint32_t))) return VB2_ERROR_BUFFER_UNALIGNED; /* Sanity check key array size */ key->arrsize = buf32[0]; if (key->arrsize * sizeof(uint32_t) != vb2_rsa_sig_size(packed_key->algorithm)) return VB2_ERROR_BAD_KEY; key->n0inv = buf32[1]; /* Arrays point inside the key data */ key->n = buf32 + 2; key->rr = buf32 + 2 + key->arrsize; key->algorithm = packed_key->algorithm; return VB2_SUCCESS; } int vb2_verify_data(const uint8_t *data, uint32_t size, struct vb2_signature *sig, const struct vb2_public_key *key, struct vb2_workbuf *wb) { struct vb2_workbuf wblocal = *wb; struct vb2_digest_context *dc; uint8_t *digest; uint32_t digest_size; int rv; if (key->algorithm >= VB2_ALG_COUNT) return VB2_ERROR_BAD_ALGORITHM; if (sig->sig_size != vb2_rsa_sig_size(key->algorithm)) { VB2_DEBUG("Wrong data signature size for algorithm, " "sig_size=%d, expected %d for algorithm %d.\n", (int)sig->sig_size, vb2_rsa_sig_size(key->algorithm), key->algorithm); return VB2_ERROR_BAD_SIGNATURE; } if (sig->data_size > size) { VB2_DEBUG("Data buffer smaller than length of signed data.\n"); return VB2_ERROR_UNKNOWN; } /* Digest goes at start of work buffer */ digest_size = vb2_digest_size(key->algorithm); digest = vb2_workbuf_alloc(&wblocal, digest_size); if (!digest) return VB2_ERROR_WORKBUF_TOO_SMALL; /* Hashing requires temp space for the context */ dc = vb2_workbuf_alloc(&wblocal, sizeof(*dc)); if (!dc) return VB2_ERROR_WORKBUF_TOO_SMALL; rv = vb2_digest_init(dc, key->algorithm); if (rv) return rv; rv = vb2_digest_extend(dc, data, sig->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 vb2_verify_digest(key, vb2_signature_data(sig), digest, &wblocal); } int vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, const struct vb2_public_key *key, struct vb2_workbuf *wb) { struct vb2_signature *sig; int rv; /* Sanity checks before attempting signature of data */ if(size < sizeof(*block)) { VB2_DEBUG("Not enough space for key block header.\n"); return VB2_ERROR_BAD_KEYBLOCK; } if (memcmp(block->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE)) { VB2_DEBUG("Not a valid verified boot key block.\n"); return VB2_ERROR_BAD_KEYBLOCK; } if (block->header_version_major != KEY_BLOCK_HEADER_VERSION_MAJOR) { VB2_DEBUG("Incompatible key block header version.\n"); return VB2_ERROR_BAD_KEYBLOCK; } if (size < block->keyblock_size) { VB2_DEBUG("Not enough data for key block.\n"); return VB2_ERROR_BAD_KEYBLOCK; } /* Check signature */ sig = &block->keyblock_signature; if (vb2_verify_signature_inside(block, block->keyblock_size, sig)) { VB2_DEBUG("Key block signature off end of block\n"); return VB2_ERROR_BAD_KEYBLOCK; } /* Make sure advertised signature data sizes are sane. */ if (block->keyblock_size < sig->data_size) { VB2_DEBUG("Signature calculated past end of block\n"); return VB2_ERROR_BAD_KEYBLOCK; } VB2_DEBUG("Checking key block signature...\n"); rv = vb2_verify_data((const uint8_t *)block, size, sig, key, wb); if (rv) { VB2_DEBUG("Invalid key block signature.\n"); return VB2_ERROR_BAD_SIGNATURE; } /* Verify we signed enough data */ if (sig->data_size < sizeof(struct vb2_keyblock)) { VB2_DEBUG("Didn't sign enough data\n"); return VB2_ERROR_BAD_KEYBLOCK; } /* Verify data key is inside the block and inside signed data */ if (vb2_verify_packed_key_inside(block, block->keyblock_size, &block->data_key)) { VB2_DEBUG("Data key off end of key block\n"); return VB2_ERROR_BAD_KEYBLOCK; } if (vb2_verify_packed_key_inside(block, sig->data_size, &block->data_key)) { VB2_DEBUG("Data key off end of signed data\n"); return VB2_ERROR_BAD_KEYBLOCK; } /* Success */ return VB2_SUCCESS; } int vb2_verify_fw_preamble(struct vb2_fw_preamble *preamble, uint32_t size, const struct vb2_public_key *key, struct vb2_workbuf *wb) { struct vb2_signature *sig = &preamble->preamble_signature; VB2_DEBUG("Verifying preamble.\n"); /* Sanity checks before attempting signature of data */ if(size < EXPECTED_VB2FIRMWAREPREAMBLEHEADER2_1_SIZE) { VB2_DEBUG("Not enough data for preamble header 2.1.\n"); return VB2_ERROR_BAD_PREAMBLE; } if (preamble->header_version_major != FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR) { VB2_DEBUG("Incompatible firmware preamble header version.\n"); return VB2_ERROR_BAD_PREAMBLE; } if (preamble->header_version_minor < 1) { VB2_DEBUG("Only preamble header 2.1+ supported\n"); return VB2_ERROR_BAD_PREAMBLE; } if (size < preamble->preamble_size) { VB2_DEBUG("Not enough data for preamble.\n"); return VB2_ERROR_BAD_PREAMBLE; } /* Check signature */ if (vb2_verify_signature_inside(preamble, preamble->preamble_size, sig)) { VB2_DEBUG("Preamble signature off end of preamble\n"); return VB2_ERROR_BAD_PREAMBLE; } /* Make sure advertised signature data sizes are sane. */ if (preamble->preamble_size < sig->data_size) { VB2_DEBUG("Signature calculated past end of the block\n"); return VB2_ERROR_BAD_PREAMBLE; } if (vb2_verify_data((const uint8_t *)preamble, size, sig, key, wb)) { VB2_DEBUG("Preamble signature validation failed\n"); return VB2_ERROR_BAD_SIGNATURE; } /* Verify we signed enough data */ if (sig->data_size < sizeof(struct vb2_fw_preamble)) { VB2_DEBUG("Didn't sign enough data\n"); return VB2_ERROR_BAD_PREAMBLE; } /* Verify body signature is inside the signed data */ if (vb2_verify_signature_inside(preamble, sig->data_size, &preamble->body_signature)) { VB2_DEBUG("Firmware body signature off end of preamble\n"); return VB2_ERROR_BAD_PREAMBLE; } /* Verify kernel subkey is inside the signed data */ if (vb2_verify_packed_key_inside(preamble, sig->data_size, &preamble->kernel_subkey)) { VB2_DEBUG("Kernel subkey off end of preamble\n"); return VB2_ERROR_BAD_PREAMBLE; } /* Success */ return VB2_SUCCESS; }