diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | firmware/2lib/2packed_key2.c | 57 | ||||
-rw-r--r-- | firmware/2lib/include/2common.h | 14 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 77 | ||||
-rw-r--r-- | firmware/2lib/include/2struct.h | 52 | ||||
-rw-r--r-- | host/lib/host_key2.c | 419 | ||||
-rw-r--r-- | host/lib/include/host_key2.h | 144 | ||||
-rw-r--r-- | tests/vb2_host_key_tests.c | 257 |
8 files changed, 996 insertions, 28 deletions
@@ -376,6 +376,7 @@ UTILLIB_SRCS = \ ifneq (${VBOOT2},) UTILLIB_SRCS += \ + host/lib/host_key2.c \ host/lib/host_misc2.c \ endif @@ -632,6 +633,7 @@ TEST_NAMES += \ tests/vb2_common_tests \ tests/vb2_common2_tests \ tests/vb2_common3_tests \ + tests/vb2_host_key_tests \ tests/vb2_host_misc_tests \ tests/vb2_misc_tests \ tests/vb2_misc2_tests \ @@ -988,6 +990,7 @@ ${BUILD}/utility/signature_digest_utility: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/host/linktest/main: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vb2_common2_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vb2_common3_tests: LDLIBS += ${CRYPTO_LIBS} +${BUILD}/tests/vb2_host_key_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vboot_common2_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vboot_common3_tests: LDLIBS += ${CRYPTO_LIBS} @@ -1167,6 +1170,7 @@ run2tests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vb2_common_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_common2_tests ${TEST_KEYS} ${RUNTEST} ${BUILD_RUN}/tests/vb2_common3_tests ${TEST_KEYS} + ${RUNTEST} ${BUILD_RUN}/tests/vb2_host_key_tests ${TEST_KEYS} ${RUNTEST} ${BUILD_RUN}/tests/vb2_host_misc_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_misc2_tests diff --git a/firmware/2lib/2packed_key2.c b/firmware/2lib/2packed_key2.c index c009c2b1..91000dfb 100644 --- a/firmware/2lib/2packed_key2.c +++ b/firmware/2lib/2packed_key2.c @@ -9,9 +9,36 @@ #include "2common.h" #include "2rsa.h" -const uint8_t *vb2_packed_key2_data(const struct vb2_packed_key2 *key) +int vb2_unpack_key2_data(struct vb2_public_key *key, + const uint8_t *key_data, + uint32_t key_size) { - return (const uint8_t *)key + key->key_offset; + const uint32_t *buf32 = (const uint32_t *)key_data; + uint32_t expected_key_size = vb2_packed_key_size(key->sig_alg); + + /* Make sure buffer is the correct length */ + if (!expected_key_size || expected_key_size != key_size) { + VB2_DEBUG("Wrong key size for algorithm\n"); + return VB2_ERROR_UNPACK_KEY_SIZE; + } + + /* Check for alignment */ + if (!vb2_aligned(buf32, sizeof(uint32_t))) + return VB2_ERROR_UNPACK_KEY_ALIGN; + + key->arrsize = buf32[0]; + + /* Sanity check key array size */ + if (key->arrsize * sizeof(uint32_t) != vb2_rsa_sig_size(key->sig_alg)) + return VB2_ERROR_UNPACK_KEY_ARRAY_SIZE; + + key->n0inv = buf32[1]; + + /* Arrays point inside the key data */ + key->n = buf32 + 2; + key->rr = buf32 + 2 + key->arrsize; + + return VB2_SUCCESS; } int vb2_unpack_key2(struct vb2_public_key *key, @@ -20,8 +47,6 @@ int vb2_unpack_key2(struct vb2_public_key *key, { const struct vb2_packed_key2 *pkey = (const struct vb2_packed_key2 *)buf; - const uint32_t *buf32; - uint32_t expected_key_size; uint32_t sig_size; uint32_t min_offset = 0; int rv; @@ -65,25 +90,11 @@ int vb2_unpack_key2(struct vb2_public_key *key, if (!vb2_digest_size(key->hash_alg)) return VB2_ERROR_UNPACK_KEY_HASH_ALGORITHM; - expected_key_size = vb2_packed_key_size(key->sig_alg); - if (!expected_key_size || expected_key_size != pkey->key_size) { - VB2_DEBUG("Wrong key size for algorithm\n"); - return VB2_ERROR_UNPACK_KEY_SIZE; - } - - /* Unpack key data */ - buf32 = (const uint32_t *)vb2_packed_key2_data(pkey); - - /* Sanity check key array size */ - key->arrsize = buf32[0]; - if (key->arrsize * sizeof(uint32_t) != sig_size) - return VB2_ERROR_UNPACK_KEY_ARRAY_SIZE; - - key->n0inv = buf32[1]; - - /* Arrays point inside the key data */ - key->n = buf32 + 2; - key->rr = buf32 + 2 + key->arrsize; + rv = vb2_unpack_key2_data(key, + (const uint8_t *)pkey + pkey->key_offset, + pkey->key_size); + if (rv) + return rv; /* Key description */ if (pkey->c.desc_size) diff --git a/firmware/2lib/include/2common.h b/firmware/2lib/include/2common.h index d26ccfce..a0c82d3c 100644 --- a/firmware/2lib/include/2common.h +++ b/firmware/2lib/include/2common.h @@ -289,6 +289,20 @@ int vb2_unpack_key2(struct vb2_public_key *key, uint32_t size); /** + * Unpack the RSA data fields for a public key + * + * This is called by vb2_unpack_key2() to extract the arrays from a packed key. + * These elements of *key will point inside the key_data buffer. + * + * @param key Destination key for RSA data fields + * @param key_data Packed key data (from inside a packed key buffer) + * @param key_size Size of packed key data in bytes + */ +int vb2_unpack_key2_data(struct vb2_public_key *key, + const uint8_t *key_data, + uint32_t key_size); + +/** * Return expected signature size for a signature/hash algorithm pair * * @param sig_alg Signature algorithm diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index 69a9494f..deae9140 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -473,6 +473,83 @@ enum vb2_return_code { VB2_ERROR_WRITE_FILE_DATA, /********************************************************************** + * Errors generated by host library key functions + */ + VB2_ERROR_HOST_KEY = VB2_ERROR_HOST_BASE + 0x020000, + + /* Unable to allocate key in vb2_private_key_read_pem() */ + VB2_ERROR_READ_PEM_ALLOC, + + /* Unable to open .pem file in vb2_private_key_read_pem() */ + VB2_ERROR_READ_PEM_FILE_OPEN, + + /* Bad RSA data from .pem file in vb2_private_key_read_pem() */ + VB2_ERROR_READ_PEM_RSA, + + /* Unable to set private key description */ + VB2_ERROR_PRIVATE_KEY_SET_DESC, + + /* Bad magic number in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_MAGIC, + + /* Bad common header in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_HEADER, + + /* Bad key data in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_DATA, + + /* Bad struct version in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_STRUCT_VERSION, + + /* Unable to allocate buffer in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_ALLOC, + + /* Unable to unpack RSA key in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_RSA, + + /* Unable to set description in vb2_private_key_unpack() */ + VB2_ERROR_UNPACK_PRIVATE_KEY_DESC, + + /* Unable to create RSA data in vb2_private_key_write() */ + VB2_ERROR_PRIVATE_KEY_WRITE_RSA, + + /* Unable to allocate packed key buffer in vb2_private_key_write() */ + VB2_ERROR_PRIVATE_KEY_WRITE_ALLOC, + + /* Unable to write file in vb2_private_key_write() */ + VB2_ERROR_PRIVATE_KEY_WRITE_FILE, + + /* Unable to determine key size in vb2_public_key_alloc() */ + VB2_ERROR_PUBLIC_KEY_ALLOC_SIZE, + + /* Unable to allocate buffer in vb2_public_key_alloc() */ + VB2_ERROR_PUBLIC_KEY_ALLOC, + + /* Unable to set public key description */ + VB2_ERROR_PUBLIC_KEY_SET_DESC, + + /* Unable to read key data in vb2_public_key_read_keyb() */ + VB2_ERROR_READ_KEYB_DATA, + + /* Wrong amount of data read in vb2_public_key_read_keyb() */ + VB2_ERROR_READ_KEYB_SIZE, + + /* Unable to allocate key buffer in vb2_public_key_read_keyb() */ + VB2_ERROR_READ_KEYB_ALLOC, + + /* Error unpacking RSA arrays in vb2_public_key_read_keyb() */ + VB2_ERROR_READ_KEYB_UNPACK, + + /* Unable to read key data in vb2_packed_key_read() */ + VB2_ERROR_READ_PACKED_KEY_DATA, + + /* Bad key data in vb2_packed_key_read() */ + VB2_ERROR_READ_PACKED_KEY, + + /* Unable to determine key size in vb2_public_key_pack() */ + VB2_ERROR_PUBLIC_KEY_PACK_SIZE, + + /********************************************************************** * Highest non-zero error generated inside vboot library. Note that * error codes passed through vboot when it calls external APIs may * still be outside this range. diff --git a/firmware/2lib/include/2struct.h b/firmware/2lib/include/2struct.h index dd956219..e4aaf607 100644 --- a/firmware/2lib/include/2struct.h +++ b/firmware/2lib/include/2struct.h @@ -208,19 +208,22 @@ struct vb2_fw_preamble { */ enum vb2_struct_common_magic { /* "Vb2B" = vb2_keyblock2.c.magic */ - VB2_MAGIC_KEYBLOCK2 = 0x42326256, + VB2_MAGIC_KEYBLOCK2 = 0x42326256, /* "Vb2F" = vb2_fw_preamble.c.magic */ - VB2_MAGIC_FW_PREAMBLE2 = 0x46326256, + VB2_MAGIC_FW_PREAMBLE2 = 0x46326256, + + /* "Vb2I" = vb2_packed_private_key2.c.magic */ + VB2_MAGIC_PACKED_PRIVATE_KEY2 = 0x49326256, /* "Vb2K" = vb2_kernel_preamble.c.magic */ - VB2_MAGIC_KERNEL_PREAMBLE2 = 0x4b326256, + VB2_MAGIC_KERNEL_PREAMBLE2 = 0x4b326256, /* "Vb2P" = vb2_packed_key2.c.magic */ - VB2_MAGIC_PACKED_KEY2 = 0x50326256, + VB2_MAGIC_PACKED_KEY2 = 0x50326256, /* "Vb2S" = vb2_signature.c.magic */ - VB2_MAGIC_SIGNATURE2 = 0x53326256, + VB2_MAGIC_SIGNATURE2 = 0x53326256, }; @@ -356,6 +359,45 @@ struct vb2_packed_key2 { #define EXPECTED_VB2_PACKED_KEY2_SIZE \ (EXPECTED_VB2_STRUCT_COMMON_SIZE + EXPECTED_GUID_SIZE + 16) +/* Current version of vb2_packed_private_key2 struct */ +#define VB2_PACKED_PRIVATE_KEY2_VERSION_MAJOR 3 +#define VB2_PACKED_PRIVATE_KEY2_VERSION_MINOR 0 + +/* + * Packed private key data, version 2 + * + * The key data must be arranged like this: + * 1) vb2_packed_private_key2 header struct h + * 2) Key description (pointed to by h.c.fixed_size) + * 3) Key data key (pointed to by h.key_offset) + */ +struct vb2_packed_private_key2 { + /* Common header fields */ + struct vb2_struct_common c; + + /* Offset of key data from start of this struct */ + uint32_t key_offset; + + /* Size of key data in bytes (NOT strength of key in bits) */ + uint32_t key_size; + + /* Signature algorithm used by the key (enum vb2_signature_algorithm) */ + uint16_t sig_alg; + + /* + * Hash digest algorithm used with the key (enum vb2_hash_algorithm). + * This is explicitly specified as part of the key to prevent use of a + * strong key with a weak hash. + */ + uint16_t hash_alg; + + /* Key GUID */ + struct vb2_guid guid; +} __attribute__((packed)); + +#define EXPECTED_VB2_PACKED_PRIVATE_KEY2_SIZE \ + (EXPECTED_VB2_STRUCT_COMMON_SIZE + EXPECTED_GUID_SIZE + 12) + /* Current version of vb2_signature2 struct */ #define VB2_SIGNATURE2_VERSION_MAJOR 3 #define VB2_SIGNATURE2_VERSION_MINOR 0 diff --git a/host/lib/host_key2.c b/host/lib/host_key2.c new file mode 100644 index 00000000..eb252ef1 --- /dev/null +++ b/host/lib/host_key2.c @@ -0,0 +1,419 @@ +/* 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. + * + * Host functions for keys. + */ + +#define OPENSSL_NO_SHA +#include <openssl/engine.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/x509.h> + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" +#include "host_common.h" +#include "host_key2.h" +#include "host_misc.h" + +void vb2_private_key_free(struct vb2_private_key *key) +{ + if (!key) + return; + + if (key->rsa_private_key) + RSA_free(key->rsa_private_key); + + if (key->desc) + free(key->desc); + + free(key); +} + +int vb2_private_key_unpack(struct vb2_private_key **key_ptr, + const uint8_t *buf, + uint32_t size) +{ + const struct vb2_packed_private_key2 *pkey = + (const struct vb2_packed_private_key2 *)buf; + struct vb2_private_key *key; + const unsigned char *start; + uint32_t min_offset = 0; + + *key_ptr = NULL; + + /* + * Check magic number. + * + * TODO: If it doesn't match, pass through to the old packed key format. + */ + if (pkey->c.magic != VB2_MAGIC_PACKED_PRIVATE_KEY2) + return VB2_ERROR_UNPACK_PRIVATE_KEY_MAGIC; + + if (vb2_verify_common_header(buf, size)) + return VB2_ERROR_UNPACK_PRIVATE_KEY_HEADER; + + /* Make sure key data is inside */ + if (vb2_verify_common_member(pkey, &min_offset, + pkey->key_offset, pkey->key_size)) + return VB2_ERROR_UNPACK_PRIVATE_KEY_DATA; + + /* + * 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 (pkey->c.struct_version_major != + VB2_PACKED_PRIVATE_KEY2_VERSION_MAJOR) + return VB2_ERROR_UNPACK_PRIVATE_KEY_STRUCT_VERSION; + + /* Allocate the new key */ + key = malloc(sizeof(*key)); + if (!key) + return VB2_ERROR_UNPACK_PRIVATE_KEY_ALLOC; + + memset(key, 0, sizeof(*key)); + + /* Copy key algorithms and guid */ + key->sig_alg = pkey->sig_alg; + key->hash_alg = pkey->hash_alg; + key->guid = pkey->guid; + + /* Unpack RSA key */ + start = (const unsigned char *)(buf + pkey->key_offset); + key->rsa_private_key = d2i_RSAPrivateKey(0, &start, pkey->key_size); + if (!key->rsa_private_key) { + free(key); + return VB2_ERROR_UNPACK_PRIVATE_KEY_RSA; + } + + /* Key description */ + if (pkey->c.desc_size) { + if (vb2_private_key_set_desc( + key, (const char *)(buf + pkey->c.fixed_size))) { + vb2_private_key_free(key); + return VB2_ERROR_UNPACK_PRIVATE_KEY_DESC; + } + } + + *key_ptr = key; + return VB2_SUCCESS; +} + +int vb2_private_key_read(struct vb2_private_key **key_ptr, + const char *filename) +{ + uint32_t size = 0; + uint8_t *buf; + int rv; + + *key_ptr = NULL; + + rv = vb2_read_file(filename, &buf, &size); + if (rv) + return rv; + + rv = vb2_private_key_unpack(key_ptr, buf, size); + + free(buf); + + return rv; +} + +int vb2_private_key_read_pem(struct vb2_private_key **key_ptr, + const char *filename) +{ + struct vb2_private_key *key; + FILE *f; + + *key_ptr = NULL; + + /* Allocate the new key */ + key = malloc(sizeof(*key)); + if (!key) + return VB2_ERROR_READ_PEM_ALLOC; + + memset(key, 0, sizeof(*key)); + + /* Read private key */ + f = fopen(filename, "r"); + if (!f) { + free(key); + return VB2_ERROR_READ_PEM_FILE_OPEN; + } + + key->rsa_private_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL); + if (!key->rsa_private_key) { + free(key); + return VB2_ERROR_READ_PEM_RSA; + } + + *key_ptr = key; + return VB2_SUCCESS; +} + +int vb2_private_key_set_desc(struct vb2_private_key *key, const char *desc) +{ + if (key->desc) + free(key->desc); + + if (desc) { + key->desc = strdup(desc); + if (!key->desc) + return VB2_ERROR_PRIVATE_KEY_SET_DESC; + } else { + key->desc = NULL; + } + + return VB2_SUCCESS; +} + +int vb2_private_key_write(const struct vb2_private_key *key, + const char *filename) +{ + struct vb2_packed_private_key2 pkey = { + .c.magic = VB2_MAGIC_PACKED_PRIVATE_KEY2, + .c.struct_version_major = VB2_PACKED_PRIVATE_KEY2_VERSION_MAJOR, + .c.struct_version_minor = VB2_PACKED_PRIVATE_KEY2_VERSION_MINOR, + .c.fixed_size = sizeof(pkey), + .sig_alg = key->sig_alg, + .hash_alg = key->hash_alg, + .guid = key->guid, + }; + uint8_t *buf; + uint8_t *rsabuf = NULL; + int rsalen; + int rv; + + memcpy(&pkey.guid, &key->guid, sizeof(pkey.guid)); + + if (key->desc) + pkey.c.desc_size = roundup32(strlen(key->desc) + 1); + + /* Pack RSA key */ + rsalen = i2d_RSAPrivateKey(key->rsa_private_key, &rsabuf); + if (rsalen <= 0) + return VB2_ERROR_PRIVATE_KEY_WRITE_RSA; + + pkey.key_offset = pkey.c.fixed_size + pkey.c.desc_size; + pkey.key_size = roundup32(rsalen); + pkey.c.total_size = pkey.key_offset + pkey.key_size; + + /* Pack private key */ + buf = malloc(pkey.c.total_size); + if (!buf) { + free(rsabuf); + return VB2_ERROR_PRIVATE_KEY_WRITE_ALLOC; + } + + memset(buf, 0, pkey.c.total_size); + memcpy(buf, &pkey, sizeof(pkey)); + + /* strcpy() is ok here because we checked the length above */ + if (key->desc) + strcpy((char *)buf + pkey.c.fixed_size, key->desc); + + memcpy(buf + pkey.key_offset, rsabuf, rsalen); + free(rsabuf); + + rv = vb2_write_object(filename, buf); + free(buf); + + return rv ? VB2_ERROR_PRIVATE_KEY_WRITE_FILE : VB2_SUCCESS; +} + +/** + * Allocate a public key buffer of sufficient size for the signature algorithm. + * + * This only initializes the sig_alg field and the guid field to an empty + * guid. It does not set any of the other fields in *key_ptr. + * + * @param key_ptr Destination for newly allocated key; this must be + * freed with vb2_public_key_free(). + * @param sig_alg Signature algorithm for key. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +static int vb2_public_key_alloc(struct vb2_public_key **key_ptr, + enum vb2_signature_algorithm sig_alg) +{ + struct vb2_public_key *key; + uint32_t key_data_size = vb2_packed_key_size(sig_alg); + + /* The buffer contains the key, its GUID, and its packed data */ + uint32_t buf_size = sizeof(*key) + sizeof(struct vb2_guid) + + key_data_size; + + if (!key_data_size) + return VB2_ERROR_PUBLIC_KEY_ALLOC_SIZE; + + key = malloc(buf_size); + if (!key) + return VB2_ERROR_PUBLIC_KEY_ALLOC; + + memset(key, 0, buf_size); + key->guid = (struct vb2_guid *)(key + 1); + key->sig_alg = sig_alg; + + *key_ptr = key; + + return VB2_SUCCESS; +} + +void vb2_public_key_free(struct vb2_public_key *key) +{ + if (key->desc) + free((void *)key->desc); + + free(key); +} + +/** + * Return the packed data for a key allocated with vb2_public_key_alloc(). + * + * The packed data is in the same buffer, following the key struct and GUID. + */ +static uint8_t *vb2_public_key_packed_data(struct vb2_public_key *key) +{ + return (uint8_t *)(key->guid + 1); +} + +int vb2_public_key_read_keyb(struct vb2_public_key **key_ptr, + const char *filename) +{ + struct vb2_public_key *key = NULL; + uint8_t *key_data, *key_buf; + uint32_t key_size; + enum vb2_signature_algorithm sig_alg; + + *key_ptr = NULL; + + if (vb2_read_file(filename, &key_data, &key_size)) + return VB2_ERROR_READ_KEYB_DATA; + + /* Guess the signature algorithm from the key size */ + for (sig_alg = VB2_SIG_RSA1024; sig_alg <= VB2_SIG_RSA8192; sig_alg++) { + if (key_size == vb2_packed_key_size(sig_alg)) + break; + } + if (sig_alg > VB2_SIG_RSA8192) { + free(key_data); + return VB2_ERROR_READ_KEYB_SIZE; + } + + if (vb2_public_key_alloc(&key, sig_alg)) { + free(key_data); + return VB2_ERROR_READ_KEYB_ALLOC; + } + + /* Copy data from the file buffer to the public key buffer */ + key_buf = vb2_public_key_packed_data(key); + memcpy(key_buf, key_data, key_size); + free(key_data); + + if (vb2_unpack_key2_data(key, key_buf, key_size)) { + vb2_public_key_free(key); + return VB2_ERROR_READ_KEYB_UNPACK; + } + + *key_ptr = key; + + return VB2_SUCCESS; +} + +int vb2_public_key_set_desc(struct vb2_public_key *key, const char *desc) +{ + if (key->desc) + free((void *)key->desc); + + if (desc) { + key->desc = strdup(desc); + if (!key->desc) + return VB2_ERROR_PUBLIC_KEY_SET_DESC; + } else { + key->desc = NULL; + } + + return VB2_SUCCESS; +} + +int vb2_packed_key2_read(struct vb2_packed_key2 **key_ptr, + const char *filename) +{ + struct vb2_public_key key; + uint8_t *buf; + uint32_t size; + + *key_ptr = NULL; + + if (!vb2_read_file(filename, &buf, &size)) + return VB2_ERROR_READ_PACKED_KEY_DATA; + + /* Sanity check: make sure key unpacks properly */ + if (!vb2_unpack_key(&key, buf, size)) + return VB2_ERROR_READ_PACKED_KEY; + + *key_ptr = (struct vb2_packed_key2 *)buf; + + return VB2_SUCCESS; +} + +int vb2_public_key_pack(struct vb2_packed_key2 **key_ptr, + const struct vb2_public_key *pubk) +{ + struct vb2_packed_key2 key = { + .c.magic = VB2_MAGIC_PACKED_KEY2, + .c.struct_version_major = VB2_PACKED_KEY2_VERSION_MAJOR, + .c.struct_version_minor = VB2_PACKED_KEY2_VERSION_MINOR, + }; + uint8_t *buf; + uint32_t *buf32; + + *key_ptr = NULL; + + /* Calculate sizes and offsets */ + key.c.fixed_size = sizeof(key); + + if (pubk->desc) + key.c.desc_size = roundup32(strlen(pubk->desc) + 1); + + key.key_offset = key.c.fixed_size + key.c.desc_size; + + key.key_size = vb2_packed_key_size(pubk->sig_alg); + if (!key.key_size) + return VB2_ERROR_PUBLIC_KEY_PACK_SIZE; + + key.c.total_size = key.key_offset + key.key_size; + + /* Copy/initialize fields */ + key.key_version = pubk->version; + key.sig_alg = pubk->sig_alg; + key.hash_alg = pubk->hash_alg; + key.guid = *pubk->guid; + + /* Allocate the new buffer */ + buf = malloc(key.c.total_size); + memset(buf, 0, key.c.total_size); + + /* Copy data into the buffer */ + memcpy(buf, &key, sizeof(key)); + + /* strcpy() is safe because we allocated above based on strlen() */ + if (pubk->desc) { + strcpy((char *)(buf + key.c.fixed_size), pubk->desc); + buf[key.c.fixed_size + key.c.desc_size - 1] = 0; + } + + /* Re-pack the key arrays */ + buf32 = (uint32_t *)(buf + key.key_offset); + buf32[0] = pubk->arrsize; + buf32[1] = pubk->n0inv; + memcpy(buf32 + 2, pubk->n, pubk->arrsize * sizeof(uint32_t)); + memcpy(buf32 + 2 + pubk->arrsize, pubk->rr, + pubk->arrsize * sizeof(uint32_t)); + + *key_ptr = (struct vb2_packed_key2 *)buf; + + return VB2_SUCCESS; +} diff --git a/host/lib/include/host_key2.h b/host/lib/include/host_key2.h new file mode 100644 index 00000000..08f55a37 --- /dev/null +++ b/host/lib/include/host_key2.h @@ -0,0 +1,144 @@ +/* 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. + * + * Host-side functions for verified boot key structures + */ + +#ifndef VBOOT_REFERENCE_HOST_KEY2_H_ +#define VBOOT_REFERENCE_HOST_KEY2_H_ + +#include "2struct.h" + +/* Private key data, in-memory format for use in signing calls. */ +struct vb2_private_key { + struct rsa_st *rsa_private_key; /* Private key data */ + enum vb2_hash_algorithm hash_alg; /* Hash algorithm */ + enum vb2_signature_algorithm sig_alg; /* Signature algorithm */ + char *desc; /* Description */ + struct vb2_guid guid; /* Key GUID */ +}; + +/** + * Free a private key. + * + * @param key Key containing internal data to free. + */ +void vb2_private_key_free(struct vb2_private_key *key); + +/** + * Unpack a private key from vb2_packed_private_key2 format. + * + * @param key_ptr Destination for newly allocated key; this must be + * freed with vb2_private_key_free(). + * @param buf Source buffer containing packed key + * @param size Size of buffer in bytes + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_private_key_unpack(struct vb2_private_key **key_ptr, + const uint8_t *buf, + uint32_t size); + +/** + * Read a private key from vb2_packed_private_key2 format. + * + * @param key_ptr Destination for newly allocated key; this must be + * freed with vb2_private_key_free(). + * @param filename File to read key data from. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_private_key_read(struct vb2_private_key **key_ptr, + const char *filename); + +/** + * Read a private key from a .pem file. + * + * This only reads the internal data for the key. It does not set any of the + * other fields in *key_ptr, since those are not contained in the .pem file. + * + * @param key_ptr Destination for newly allocated key; this must be + * freed with vb2_private_key_free(). + * @param filename File to read key data from. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_private_key_read_pem(struct vb2_private_key **key_ptr, + const char *filename); + +/** + * Set the description of a private key. + * + * @param key Key to set description for + * @param desc Description string, or NULL if no description. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_private_key_set_desc(struct vb2_private_key *key, const char *desc); + +/** + * Write a private key to vb2_packed_private_key2 format. + * + * @param key Key to write + * @param filename File to write key data to. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_private_key_write(const struct vb2_private_key *key, + const char *filename); + +/** + * Free a public key allocated by one of the functions below. + * + * Note that this should ONLY be called for public keys allocated via one + * of those functions; public keys created or filled in other ways (such as + * vb2_unpack_key2()) do not allocate memory for sub-fields in the same way. + * + * @param key Key to free + */ +void vb2_public_key_free(struct vb2_public_key *key); + +/** + * Read a public key from a .keyb file. + * + * Guesses the signature algorithm based on the size of the .keyb file. Does + * not set the hash_alg, guid, or desc fields, since those are not contained in + * the .keyb file. + * + * @param key_ptr Destination for newly allocated key; this must be + * freed with vb2_public_key_free(). + * @param filename File to read key from. + * @return VB2_SUCCESS, or non-zero error code if error. + */ + +int vb2_public_key_read_keyb(struct vb2_public_key **key_ptr, + const char *filename); + +/** + * Set the description of a public key. + * + * @param key Key to set description for + * @param desc Description string, or NULL if no description. + * @return VB2_SUCCESS, or non-zero error code if error. + */ +int vb2_public_key_set_desc(struct vb2_public_key *key, const char *desc); + +/** + * Read a public key in vb2_packed_key2 format. + * + * @param key_ptr On success, points to the newly allocated key buffer. + * Caller is responsible for calling free() on this. + * @return VB2_SUCCESS, or non-zero if error. + */ +int vb2_packed_key2_read(struct vb2_packed_key2 **key_ptr, + const char *filename); + +/** + * Pack a public key into vb2_packed_key2 format. + * + * @param pubk Public key to pack + * @param key_ptr On success, points to a newly allocated packed key + * buffer. Caller is responsible for calling free() on + * this. + * @return VB2_SUCCESS, or non-zero if error. + */ +int vb2_public_key_pack(struct vb2_packed_key2 **key_ptr, + const struct vb2_public_key *pubk); + +#endif /* VBOOT_REFERENCE_HOST_KEY2_H_ */ diff --git a/tests/vb2_host_key_tests.c b/tests/vb2_host_key_tests.c new file mode 100644 index 00000000..9a56351c --- /dev/null +++ b/tests/vb2_host_key_tests.c @@ -0,0 +1,257 @@ +/* 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. + * + * Tests for host library vboot2 key functions + */ + +#include <stdio.h> +#include <unistd.h> + +#include "2sysincludes.h" +#include "2common.h" +#include "2rsa.h" +#include "host_common.h" +#include "host_key2.h" + +#include "test_common.h" + +/* Test only the algorithms we use */ +struct alg_combo { + const char *name; + enum vb2_signature_algorithm sig_alg; + enum vb2_hash_algorithm hash_alg; +}; + +static const struct alg_combo test_algs[] = { + {"RSA2048/SHA-256", VB2_SIG_RSA2048, VB2_HASH_SHA256}, + {"RSA4096/SHA-256", VB2_SIG_RSA4096, VB2_HASH_SHA256}, + {"RSA8192/SHA-512", VB2_SIG_RSA8192, VB2_HASH_SHA512}, +}; + +static void private_key_tests(const struct alg_combo *combo, + const char *pemfile) +{ + struct vb2_private_key *key, *k2; + struct vb2_packed_private_key2 *pkey; + const char *testfile = "test.vbprik2"; + const char *notapem = "not_a_pem"; + const char *testdesc = "test desc"; + const struct vb2_guid test_guid = {.raw = {0xaa}}; + uint8_t *buf, *buf2; + uint32_t bufsize; + + TEST_SUCC(vb2_private_key_read_pem(&key, pemfile), "Read pem - good"); + TEST_PTR_NEQ(key, NULL, " key_ptr"); + TEST_PTR_NEQ(key->rsa_private_key, NULL, " rsa_private_key"); + TEST_PTR_EQ(key->desc, NULL, " desc"); + vb2_private_key_free(key); + + TEST_EQ(vb2_private_key_read_pem(&key, "no_such_key"), + VB2_ERROR_READ_PEM_FILE_OPEN, "Read pem - no key"); + TEST_PTR_EQ(key, NULL, " key_ptr"); + + vb2_write_file(testfile, (const uint8_t *)notapem, sizeof(notapem)); + TEST_EQ(vb2_private_key_read_pem(&key, testfile), + VB2_ERROR_READ_PEM_RSA, "Read pem - not a pem"); + unlink(testfile); + + TEST_SUCC(vb2_private_key_read_pem(&key, pemfile), "Read pem - good2"); + TEST_SUCC(vb2_private_key_set_desc(key, testdesc), "Set desc"); + TEST_PTR_NEQ(key->desc, NULL, " desc"); + TEST_PTR_NEQ(key->desc, testdesc, " made a copy"); + TEST_EQ(strcmp(key->desc, testdesc), 0, " right contents"); + TEST_SUCC(vb2_private_key_set_desc(key, NULL), "Clear desc"); + TEST_PTR_EQ(key->desc, NULL, " desc"); + TEST_SUCC(vb2_private_key_set_desc(key, testdesc), "Set desc"); + vb2_private_key_free(key); + + TEST_SUCC(vb2_private_key_read_pem(&key, pemfile), "Read pem - good3"); + TEST_SUCC(vb2_private_key_set_desc(key, testdesc), "Set desc"); + key->hash_alg = combo->hash_alg; + key->sig_alg = combo->sig_alg; + key->guid = test_guid; + + unlink(testfile); + + TEST_EQ(vb2_private_key_read(&k2, testfile), + VB2_ERROR_READ_FILE_OPEN, "Read key no file"); + TEST_EQ(vb2_private_key_write(key, "no/such/dir"), + VB2_ERROR_PRIVATE_KEY_WRITE_FILE, "Write key to bad path"); + + TEST_SUCC(vb2_private_key_write(key, testfile), "Write key good"); + TEST_SUCC(vb2_private_key_read(&k2, testfile), "Read key good"); + TEST_PTR_NEQ(k2, NULL, " key_ptr"); + TEST_EQ(k2->sig_alg, key->sig_alg, " sig alg"); + TEST_EQ(k2->hash_alg, key->hash_alg, " hash alg"); + TEST_EQ(memcmp(&k2->guid, &key->guid, sizeof(k2->guid)), 0, " guid"); + TEST_EQ(strcmp(k2->desc, testdesc), 0, " desc"); + vb2_private_key_free(k2); + + TEST_SUCC(vb2_read_file(testfile, &buf, &bufsize), "Read key raw"); + pkey = (struct vb2_packed_private_key2 *)buf; + + /* Make a backup of the good buffer so we can mangle it */ + buf2 = malloc(bufsize); + memcpy(buf2, buf, bufsize); + + TEST_SUCC(vb2_private_key_unpack(&k2, buf, bufsize), + "Unpack private key good"); + vb2_private_key_free(k2); + + memcpy(buf, buf2, bufsize); + pkey->c.magic = VB2_MAGIC_PACKED_KEY2; + TEST_EQ(vb2_private_key_unpack(&k2, buf, bufsize), + VB2_ERROR_UNPACK_PRIVATE_KEY_MAGIC, + "Unpack private key bad magic"); + TEST_PTR_EQ(k2, NULL, " key_ptr"); + + memcpy(buf, buf2, bufsize); + pkey->c.desc_size++; + TEST_EQ(vb2_private_key_unpack(&k2, buf, bufsize), + VB2_ERROR_UNPACK_PRIVATE_KEY_HEADER, + "Unpack private key bad header"); + + memcpy(buf, buf2, bufsize); + pkey->key_size += pkey->c.total_size; + TEST_EQ(vb2_private_key_unpack(&k2, buf, bufsize), + VB2_ERROR_UNPACK_PRIVATE_KEY_DATA, + "Unpack private key bad data size"); + + memcpy(buf, buf2, bufsize); + pkey->c.struct_version_major++; + TEST_EQ(vb2_private_key_unpack(&k2, buf, bufsize), + VB2_ERROR_UNPACK_PRIVATE_KEY_STRUCT_VERSION, + "Unpack private key bad struct version"); + + memcpy(buf, buf2, bufsize); + pkey->c.struct_version_minor++; + TEST_SUCC(vb2_private_key_unpack(&k2, buf, bufsize), + "Unpack private key minor version"); + vb2_private_key_free(k2); + + memcpy(buf, buf2, bufsize); + pkey->key_size -= 32; + TEST_EQ(vb2_private_key_unpack(&k2, buf, bufsize), + VB2_ERROR_UNPACK_PRIVATE_KEY_RSA, + "Unpack private key bad rsa data"); + + free(buf); + free(buf2); + unlink(testfile); + + vb2_private_key_free(key); +} + +static void public_key_tests(const struct alg_combo *combo, + const char *keybfile) +{ + struct vb2_public_key *key, k2; + struct vb2_packed_key2 *pkey; + const char *testfile = "test.vbpubk2"; + const char *testdesc = "test desc"; + const struct vb2_guid test_guid = {.raw = {0xbb}}; + const uint32_t test_version = 0xcc01; + uint8_t *buf; + uint32_t bufsize; + + TEST_EQ(vb2_public_key_read_keyb(&key, "no_such_key"), + VB2_ERROR_READ_KEYB_DATA, "Read keyb - no file"); + TEST_PTR_EQ(key, NULL, " key_ptr"); + + TEST_SUCC(vb2_public_key_read_keyb(&key, keybfile), "Read keyb - good"); + TEST_PTR_NEQ(key, NULL, " key_ptr"); + TEST_EQ(key->sig_alg, combo->sig_alg, " sig_alg"); + TEST_PTR_EQ(key->desc, NULL, " desc"); + vb2_public_key_free(key); + + bufsize = vb2_packed_key_size(combo->sig_alg); + buf = malloc(bufsize); + memset(buf, 0, bufsize); + + vb2_write_file(testfile, buf, bufsize - 1); + TEST_EQ(vb2_public_key_read_keyb(&key, testfile), + VB2_ERROR_READ_KEYB_SIZE, "Read keyb - bad size"); + unlink(testfile); + + vb2_write_file(testfile, buf, bufsize); + free(buf); + TEST_EQ(vb2_public_key_read_keyb(&key, testfile), + VB2_ERROR_READ_KEYB_UNPACK, "Read keyb - unpack"); + unlink(testfile); + + TEST_SUCC(vb2_public_key_read_keyb(&key, keybfile), "Read keyb 2"); + TEST_SUCC(vb2_public_key_set_desc(key, testdesc), "Set desc"); + TEST_PTR_NEQ(key->desc, NULL, " desc"); + TEST_PTR_NEQ(key->desc, testdesc, " made a copy"); + TEST_EQ(strcmp(key->desc, testdesc), 0, " right contents"); + TEST_SUCC(vb2_public_key_set_desc(key, NULL), "Clear desc"); + TEST_PTR_EQ(key->desc, NULL, " desc"); + TEST_SUCC(vb2_public_key_set_desc(key, testdesc), "Set desc"); + vb2_public_key_free(key); + + TEST_SUCC(vb2_public_key_read_keyb(&key, keybfile), "Read keyb 3"); + TEST_SUCC(vb2_public_key_set_desc(key, testdesc), "Set desc"); + key->hash_alg = combo->hash_alg; + key->guid = &test_guid; + key->version = test_version; + + TEST_SUCC(vb2_public_key_pack(&pkey, key), "Pack public key"); + TEST_PTR_NEQ(pkey, NULL, " key_ptr"); + TEST_EQ(pkey->hash_alg, key->hash_alg, " hash_alg"); + TEST_EQ(pkey->sig_alg, key->sig_alg, " sig_alg"); + TEST_EQ(pkey->key_version, key->version, " version"); + TEST_EQ(memcmp(&pkey->guid, key->guid, sizeof(pkey->guid)), 0, + " guid"); + TEST_EQ(strcmp((char *)pkey + pkey->c.fixed_size, key->desc), 0, + " desc"); + TEST_SUCC(vb2_unpack_key2(&k2, (uint8_t *)pkey, pkey->c.total_size), + "Unpack public key"); + TEST_EQ(key->arrsize, k2.arrsize, " arrsize"); + TEST_EQ(key->n0inv, k2.n0inv, " n0inv"); + TEST_EQ(memcmp(key->n, k2.n, key->arrsize * sizeof(uint32_t)), 0, + " n"); + TEST_EQ(memcmp(key->rr, k2.rr, key->arrsize * sizeof(uint32_t)), 0, + " rr"); + free(pkey); + + key->sig_alg = VB2_SIG_INVALID; + TEST_EQ(vb2_public_key_pack(&pkey, key), + VB2_ERROR_PUBLIC_KEY_PACK_SIZE, + "Pack invalid sig alg"); + vb2_public_key_free(key); +} + +static int test_algorithm(const struct alg_combo *combo, const char *keys_dir) +{ + int rsa_bits = vb2_rsa_sig_size(combo->sig_alg) * 8; + char pemfile[1024]; + char keybfile[1024]; + + printf("***Testing algorithm: %s\n", combo->name); + + sprintf(pemfile, "%s/key_rsa%d.pem", keys_dir, rsa_bits); + sprintf(keybfile, "%s/key_rsa%d.keyb", keys_dir, rsa_bits); + + private_key_tests(combo, pemfile); + public_key_tests(combo, keybfile); + + return 0; +} + +int main(int argc, char *argv[]) { + + if (argc == 2) { + int i; + + for (i = 0; i < ARRAY_SIZE(test_algs); i++) { + if (test_algorithm(test_algs + i, argv[1])) + return 1; + } + } else { + fprintf(stderr, "Usage: %s <keys_dir>", argv[0]); + return -1; + } + + return gTestSuccess ? 0 : 255; +} |