| /* 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. | |
| */ | |
| #include <stdio.h> | |
| #include <openssl/pem.h> | |
| #include "2sysincludes.h" | |
| #include "2common.h" | |
| #include "2rsa.h" | |
| #include "2sha.h" | |
| #include "vb21_common.h" | |
| #include "host_common.h" | |
| #include "host_key2.h" | |
| #include "host_misc.h" | |
| #include "openssl_compat.h" | |
| const struct vb2_text_vs_enum vb2_text_vs_sig[] = { | |
| {"RSA1024", VB2_SIG_RSA1024}, | |
| {"RSA2048", VB2_SIG_RSA2048}, | |
| {"RSA4096", VB2_SIG_RSA4096}, | |
| {"RSA8192", VB2_SIG_RSA8192}, | |
| {"RSA2048EXP3", VB2_SIG_RSA2048_EXP3}, | |
| {"RSA3072EXP3", VB2_SIG_RSA3072_EXP3}, | |
| {0, 0} | |
| }; | |
| const struct vb2_text_vs_enum vb2_text_vs_hash[] = { | |
| {"SHA1", VB2_HASH_SHA1}, | |
| {"SHA256", VB2_HASH_SHA256}, | |
| {"SHA512", VB2_HASH_SHA512}, | |
| {0, 0} | |
| }; | |
| const struct vb2_text_vs_enum vb2_text_vs_crypto[] = { | |
| {"RSA1024 SHA1", VB2_ALG_RSA1024_SHA1}, | |
| {"RSA1024 SHA256", VB2_ALG_RSA1024_SHA256}, | |
| {"RSA1024 SHA512", VB2_ALG_RSA1024_SHA512}, | |
| {"RSA2048 SHA1", VB2_ALG_RSA2048_SHA1}, | |
| {"RSA2048 SHA256", VB2_ALG_RSA2048_SHA256}, | |
| {"RSA2048 SHA512", VB2_ALG_RSA2048_SHA512}, | |
| {"RSA4096 SHA1", VB2_ALG_RSA4096_SHA1}, | |
| {"RSA4096 SHA256", VB2_ALG_RSA4096_SHA256}, | |
| {"RSA4096 SHA512", VB2_ALG_RSA4096_SHA512}, | |
| {"RSA8192 SHA1", VB2_ALG_RSA8192_SHA1}, | |
| {"RSA8192 SHA256", VB2_ALG_RSA8192_SHA256}, | |
| {"RSA8192 SHA512", VB2_ALG_RSA8192_SHA512}, | |
| {0, 0} | |
| }; | |
| const struct vb2_text_vs_enum *vb2_lookup_by_num( | |
| const struct vb2_text_vs_enum *table, | |
| const unsigned int num) | |
| { | |
| for (; table->name; table++) | |
| if (table->num == num) | |
| return table; | |
| return 0; | |
| } | |
| const struct vb2_text_vs_enum *vb2_lookup_by_name( | |
| const struct vb2_text_vs_enum *table, | |
| const char *name) | |
| { | |
| for (; table->name; table++) | |
| if (!strcasecmp(table->name, name)) | |
| return table; | |
| return 0; | |
| } | |
| const char *vb2_get_sig_algorithm_name(enum vb2_signature_algorithm sig_alg) | |
| { | |
| const struct vb2_text_vs_enum *entry = | |
| vb2_lookup_by_num(vb2_text_vs_sig, sig_alg); | |
| return entry ? entry->name : VB2_INVALID_ALG_NAME; | |
| } | |
| const char *vb2_get_crypto_algorithm_name(enum vb2_crypto_algorithm alg) | |
| { | |
| const struct vb2_text_vs_enum *entry = | |
| vb2_lookup_by_num(vb2_text_vs_crypto, alg); | |
| return entry ? entry->name : VB2_INVALID_ALG_NAME; | |
| } | |
| 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 vb21_private_key_unpack(struct vb2_private_key **key_ptr, | |
| const uint8_t *buf, | |
| uint32_t size) | |
| { | |
| const struct vb21_packed_private_key *pkey = | |
| (const struct vb21_packed_private_key *)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 != VB21_MAGIC_PACKED_PRIVATE_KEY) | |
| return VB2_ERROR_UNPACK_PRIVATE_KEY_MAGIC; | |
| if (vb21_verify_common_header(buf, size)) | |
| return VB2_ERROR_UNPACK_PRIVATE_KEY_HEADER; | |
| /* Make sure key data is inside */ | |
| if (vb21_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 != | |
| VB21_PACKED_PRIVATE_KEY_VERSION_MAJOR) | |
| return VB2_ERROR_UNPACK_PRIVATE_KEY_STRUCT_VERSION; | |
| /* Allocate the new key */ | |
| key = calloc(1, sizeof(*key)); | |
| if (!key) | |
| return VB2_ERROR_UNPACK_PRIVATE_KEY_ALLOC; | |
| /* Copy key algorithms and ID */ | |
| key->sig_alg = pkey->sig_alg; | |
| key->hash_alg = pkey->hash_alg; | |
| key->id = pkey->id; | |
| /* Unpack RSA key */ | |
| if (pkey->sig_alg == VB2_SIG_NONE) { | |
| if (pkey->key_size != 0) { | |
| free(key); | |
| return VB2_ERROR_UNPACK_PRIVATE_KEY_HASH; | |
| } | |
| } else { | |
| 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 vb21_private_key_read(struct vb2_private_key **key_ptr, | |
| const char *filename) | |
| { | |
| uint32_t size = 0; | |
| uint8_t *buf = NULL; | |
| int rv; | |
| *key_ptr = NULL; | |
| rv = vb2_read_file(filename, &buf, &size); | |
| if (rv) | |
| return rv; | |
| rv = vb21_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 = calloc(1, sizeof(*key)); | |
| if (!key) | |
| return VB2_ERROR_READ_PEM_ALLOC; | |
| /* Read private key */ | |
| f = fopen(filename, "rb"); | |
| if (!f) { | |
| free(key); | |
| return VB2_ERROR_READ_PEM_FILE_OPEN; | |
| } | |
| key->rsa_private_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL); | |
| fclose(f); | |
| 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 vb21_private_key_write(const struct vb2_private_key *key, | |
| const char *filename) | |
| { | |
| struct vb21_packed_private_key pkey = { | |
| .c.magic = VB21_MAGIC_PACKED_PRIVATE_KEY, | |
| .c.struct_version_major = VB21_PACKED_PRIVATE_KEY_VERSION_MAJOR, | |
| .c.struct_version_minor = VB21_PACKED_PRIVATE_KEY_VERSION_MINOR, | |
| .c.fixed_size = sizeof(pkey), | |
| .sig_alg = key->sig_alg, | |
| .hash_alg = key->hash_alg, | |
| .id = key->id, | |
| }; | |
| uint8_t *buf; | |
| uint8_t *rsabuf = NULL; | |
| int rsalen = 0; | |
| int rv; | |
| memcpy(&pkey.id, &key->id, sizeof(pkey.id)); | |
| pkey.c.desc_size = vb2_desc_size(key->desc); | |
| if (key->sig_alg != VB2_SIG_NONE) { | |
| /* Pack RSA key */ | |
| rsalen = i2d_RSAPrivateKey(key->rsa_private_key, &rsabuf); | |
| if (rsalen <= 0 || !rsabuf) | |
| 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 = calloc(1, pkey.c.total_size); | |
| if (!buf) { | |
| free(rsabuf); | |
| return VB2_ERROR_PRIVATE_KEY_WRITE_ALLOC; | |
| } | |
| memcpy(buf, &pkey, sizeof(pkey)); | |
| /* strcpy() is ok here because we checked the length above */ | |
| if (pkey.c.desc_size) | |
| strcpy((char *)buf + pkey.c.fixed_size, key->desc); | |
| if (rsabuf) { | |
| memcpy(buf + pkey.key_offset, rsabuf, rsalen); | |
| free(rsabuf); | |
| } | |
| rv = vb21_write_object(filename, buf); | |
| free(buf); | |
| return rv ? VB2_ERROR_PRIVATE_KEY_WRITE_FILE : VB2_SUCCESS; | |
| } | |
| int vb2_private_key_hash(const struct vb2_private_key **key_ptr, | |
| enum vb2_hash_algorithm hash_alg) | |
| { | |
| *key_ptr = NULL; | |
| switch (hash_alg) { | |
| #if VB2_SUPPORT_SHA1 | |
| case VB2_HASH_SHA1: | |
| { | |
| static const struct vb2_private_key key = { | |
| .hash_alg = VB2_HASH_SHA1, | |
| .sig_alg = VB2_SIG_NONE, | |
| .desc = "Unsigned SHA1", | |
| .id = VB2_ID_NONE_SHA1, | |
| }; | |
| *key_ptr = &key; | |
| return VB2_SUCCESS; | |
| } | |
| #endif | |
| #if VB2_SUPPORT_SHA256 | |
| case VB2_HASH_SHA256: | |
| { | |
| static const struct vb2_private_key key = { | |
| .hash_alg = VB2_HASH_SHA256, | |
| .sig_alg = VB2_SIG_NONE, | |
| .desc = "Unsigned SHA-256", | |
| .id = VB2_ID_NONE_SHA256, | |
| }; | |
| *key_ptr = &key; | |
| return VB2_SUCCESS; | |
| } | |
| #endif | |
| #if VB2_SUPPORT_SHA512 | |
| case VB2_HASH_SHA512: | |
| { | |
| static const struct vb2_private_key key = { | |
| .hash_alg = VB2_HASH_SHA512, | |
| .sig_alg = VB2_SIG_NONE, | |
| .desc = "Unsigned SHA-512", | |
| .id = VB2_ID_NONE_SHA512, | |
| }; | |
| *key_ptr = &key; | |
| return VB2_SUCCESS; | |
| } | |
| #endif | |
| default: | |
| return VB2_ERROR_PRIVATE_KEY_HASH; | |
| } | |
| } | |
| 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 ID, and its packed data */ | |
| uint32_t buf_size = sizeof(*key) + sizeof(struct vb2_id) + | |
| key_data_size; | |
| if (!key_data_size) | |
| return VB2_ERROR_PUBLIC_KEY_ALLOC_SIZE; | |
| key = calloc(1, buf_size); | |
| if (!key) | |
| return VB2_ERROR_PUBLIC_KEY_ALLOC; | |
| key->id = (struct vb2_id *)(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) | |
| return; | |
| if (key->desc) | |
| free((void *)key->desc); | |
| free(key); | |
| } | |
| uint8_t *vb2_public_key_packed_data(struct vb2_public_key *key) | |
| { | |
| return (uint8_t *)(key->id + 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 | |
| * Note: This only considers exponent F4 keys, as there is no way to | |
| * distinguish between exp 3 and F4 based on size. Vboot API 2.1 is | |
| * required to make proper use of exp 3 keys. */ | |
| 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_key_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 vb21_packed_key_read(struct vb21_packed_key **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 (vb21_unpack_key(&key, buf, size)) | |
| return VB2_ERROR_READ_PACKED_KEY; | |
| *key_ptr = (struct vb21_packed_key *)buf; | |
| return VB2_SUCCESS; | |
| } | |
| int vb21_public_key_pack(struct vb21_packed_key **key_ptr, | |
| const struct vb2_public_key *pubk) | |
| { | |
| struct vb21_packed_key key = { | |
| .c.magic = VB21_MAGIC_PACKED_KEY, | |
| .c.struct_version_major = VB21_PACKED_KEY_VERSION_MAJOR, | |
| .c.struct_version_minor = VB21_PACKED_KEY_VERSION_MINOR, | |
| }; | |
| uint8_t *buf; | |
| uint32_t *buf32; | |
| *key_ptr = NULL; | |
| /* Calculate sizes and offsets */ | |
| key.c.fixed_size = sizeof(key); | |
| key.c.desc_size = vb2_desc_size(pubk->desc); | |
| key.key_offset = key.c.fixed_size + key.c.desc_size; | |
| if (pubk->sig_alg != VB2_SIG_NONE) { | |
| 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.id = *pubk->id; | |
| /* Allocate the new buffer */ | |
| buf = calloc(1, 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 && *pubk->desc) { | |
| strcpy((char *)(buf + key.c.fixed_size), pubk->desc); | |
| buf[key.c.fixed_size + key.c.desc_size - 1] = 0; | |
| } | |
| if (pubk->sig_alg != VB2_SIG_NONE) { | |
| /* 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 vb21_packed_key *)buf; | |
| return VB2_SUCCESS; | |
| } | |
| int vb2_public_key_hash(struct vb2_public_key *key, | |
| enum vb2_hash_algorithm hash_alg) | |
| { | |
| switch (hash_alg) { | |
| #if VB2_SUPPORT_SHA1 | |
| case VB2_HASH_SHA1: | |
| key->desc = "Unsigned SHA1"; | |
| break; | |
| #endif | |
| #if VB2_SUPPORT_SHA256 | |
| case VB2_HASH_SHA256: | |
| key->desc = "Unsigned SHA-256"; | |
| break; | |
| #endif | |
| #if VB2_SUPPORT_SHA512 | |
| case VB2_HASH_SHA512: | |
| key->desc = "Unsigned SHA-512"; | |
| break; | |
| #endif | |
| default: | |
| return VB2_ERROR_PUBLIC_KEY_HASH; | |
| } | |
| key->sig_alg = VB2_SIG_NONE; | |
| key->hash_alg = hash_alg; | |
| key->id = vb2_hash_id(hash_alg); | |
| return VB2_SUCCESS; | |
| } | |
| enum vb2_signature_algorithm vb2_rsa_sig_alg(struct rsa_st *rsa) | |
| { | |
| const BIGNUM *e, *n; | |
| int exp, bits; | |
| RSA_get0_key(rsa, &n, &e, NULL); | |
| exp = BN_get_word(e); | |
| bits = BN_num_bits(n); | |
| switch (exp) { | |
| case RSA_3: | |
| switch (bits) { | |
| case 2048: | |
| return VB2_SIG_RSA2048_EXP3; | |
| case 3072: | |
| return VB2_SIG_RSA3072_EXP3; | |
| } | |
| break; | |
| case RSA_F4: | |
| switch (bits) { | |
| case 1024: | |
| return VB2_SIG_RSA1024; | |
| case 2048: | |
| return VB2_SIG_RSA2048; | |
| case 4096: | |
| return VB2_SIG_RSA4096; | |
| case 8192: | |
| return VB2_SIG_RSA8192; | |
| } | |
| } | |
| /* no clue */ | |
| return VB2_SIG_INVALID; | |
| } | |
| int vb21_public_key_write(const struct vb2_public_key *key, | |
| const char *filename) | |
| { | |
| struct vb21_packed_key *pkey; | |
| int ret; | |
| ret = vb21_public_key_pack(&pkey, key); | |
| if (ret) | |
| return ret; | |
| ret = vb21_write_object(filename, pkey); | |
| free(pkey); | |
| return ret; | |
| } |