blob: f6d87e1155d53dc55880adbb3172a7a68ec2f24d [file] [log] [blame]
/* Copyright 2011 The ChromiumOS Authors
* 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 <openssl/pem.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "2common.h"
#include "2rsa.h"
#include "2sha.h"
#include "2sysincludes.h"
#include "host_common.h"
#include "host_common21.h"
#include "host_key21.h"
#include "host_key.h"
#include "host_misc.h"
#include "host_p11.h"
enum vb2_crypto_algorithm vb2_get_crypto_algorithm(
enum vb2_hash_algorithm hash_alg,
enum vb2_signature_algorithm sig_alg)
{
/* Make sure algorithms are in the range supported by crypto alg */
if (sig_alg < VB2_SIG_RSA1024 || sig_alg >= VB2_SIG_ALG_COUNT)
return VB2_ALG_COUNT;
if (hash_alg < VB2_HASH_SHA1 || hash_alg > VB2_HASH_SHA512)
return VB2_ALG_COUNT;
return (sig_alg - VB2_SIG_RSA1024)
* (VB2_HASH_SHA512 - VB2_HASH_SHA1 + 1)
+ (hash_alg - VB2_HASH_SHA1);
};
static vb2_error_t vb2_read_local_private_key(uint8_t *buf, uint32_t bufsize,
struct vb2_private_key *key)
{
uint64_t alg = *(uint64_t *)buf;
key->key_location = PRIVATE_KEY_LOCAL;
key->hash_alg = vb2_crypto_to_hash(alg);
key->sig_alg = vb2_crypto_to_signature(alg);
const unsigned char *start = buf + sizeof(alg);
key->rsa_private_key = d2i_RSAPrivateKey(0, &start, bufsize - sizeof(alg));
if (!key->rsa_private_key) {
VB2_DEBUG("Unable to parse RSA private key\n");
return VB2_ERROR_UNKNOWN;
}
return VB2_SUCCESS;
}
static vb2_error_t vb2_read_p11_private_key(const char *key_info, struct vb2_private_key *key)
{
/* The format of p11 key info: "remote:{lib_path}:{slot_id}:{key_label}" */
char *p11_lib = NULL, *p11_label = NULL;
int p11_slot_id;
vb2_error_t ret = VB2_ERROR_UNKNOWN;
if (sscanf(key_info, "remote:%m[^:]:%i:%m[^:]", &p11_lib, &p11_slot_id, &p11_label) !=
3) {
VB2_DEBUG("Failed to parse pkcs11 key info\n");
goto done;
}
if (pkcs11_init(p11_lib) != VB2_SUCCESS) {
VB2_DEBUG("Unable to initialize pkcs11 library\n");
goto done;
}
struct pkcs11_key *p11_key = malloc(sizeof(struct pkcs11_key));
if (pkcs11_get_key(p11_slot_id, p11_label, p11_key) != VB2_SUCCESS) {
VB2_DEBUG("Unable to get pkcs11 key\n");
free(p11_key);
goto done;
}
key->key_location = PRIVATE_KEY_P11;
key->p11_key = p11_key;
key->sig_alg = pkcs11_get_sig_alg(p11_key);
key->hash_alg = pkcs11_get_hash_alg(p11_key);
if (key->sig_alg == VB2_SIG_INVALID || key->hash_alg == VB2_HASH_INVALID) {
VB2_DEBUG("Unable to get signature or hash algorithm\n");
free(p11_key);
goto done;
}
ret = VB2_SUCCESS;
done:
free(p11_lib);
free(p11_label);
return ret;
}
static bool is_vb21_private_key(const uint8_t *buf, uint32_t bufsize)
{
const struct vb21_packed_private_key *pkey =
(const struct vb21_packed_private_key *)buf;
return bufsize >= sizeof(pkey->c.magic) &&
pkey->c.magic == VB21_MAGIC_PACKED_PRIVATE_KEY;
}
struct vb2_private_key *vb2_read_private_key(const char *key_info)
{
struct vb2_private_key *key = (struct vb2_private_key *)calloc(sizeof(*key), 1);
if (!key) {
VB2_DEBUG("Unable to allocate private key\n");
return NULL;
}
static const char p11_prefix[] = "remote";
static const char local_prefix[] = "local";
char *colon = strchr(key_info, ':');
if (colon) {
int prefix_size = colon - key_info;
if (!strncmp(key_info, p11_prefix, prefix_size)) {
if (vb2_read_p11_private_key(key_info, key) != VB2_SUCCESS) {
VB2_DEBUG("Unable to read pkcs11 private key\n");
free(key);
return NULL;
}
return key;
}
if (!strncmp(key_info, local_prefix, prefix_size))
key_info = colon + 1;
}
// Read the private key from local file.
uint8_t *buf = NULL;
uint32_t bufsize = 0;
if (vb2_read_file(key_info, &buf, &bufsize) != VB2_SUCCESS) {
VB2_DEBUG("unable to read from file %s\n", key_info);
return NULL;
}
vb2_error_t rv;
bool is_vb21 = is_vb21_private_key(buf, bufsize);
if (is_vb21)
rv = vb21_private_key_unpack_raw(buf, bufsize, key);
else
rv = vb2_read_local_private_key(buf, bufsize, key);
free(buf);
if (rv != VB2_SUCCESS) {
VB2_DEBUG("Unable to read local %s private key\n", is_vb21 ? "vb21" : "vb2");
free(key);
return NULL;
}
return key;
}
struct vb2_private_key *vb2_read_private_key_pem(
const char* filename,
enum vb2_crypto_algorithm algorithm)
{
if (algorithm >= VB2_ALG_COUNT) {
VB2_DEBUG("%s() called with invalid algorithm!\n",
__FUNCTION__);
return NULL;
}
/* Read private key */
FILE *f = fopen(filename, "r");
if (!f) {
VB2_DEBUG("%s(): Couldn't open key file: %s\n",
__FUNCTION__, filename);
return NULL;
}
struct rsa_st *rsa_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
fclose(f);
if (!rsa_key) {
VB2_DEBUG("%s(): Couldn't read private key from file: %s\n",
__FUNCTION__, filename);
return NULL;
}
/* Store key and algorithm in our struct */
struct vb2_private_key *key =
(struct vb2_private_key *)calloc(sizeof(*key), 1);
if (!key) {
RSA_free(rsa_key);
return NULL;
}
key->rsa_private_key = rsa_key;
key->hash_alg = vb2_crypto_to_hash(algorithm);
key->sig_alg = vb2_crypto_to_signature(algorithm);
/* Return the key */
return key;
}
void vb2_free_private_key(struct vb2_private_key *key)
{
if (!key)
return;
if (key->key_location == PRIVATE_KEY_LOCAL && key->rsa_private_key)
RSA_free(key->rsa_private_key);
else if (key->key_location == PRIVATE_KEY_P11 && key->p11_key)
pkcs11_free_key(key->p11_key);
if (key->desc)
free(key->desc);
free(key);
}
vb2_error_t vb2_write_private_key(const char *filename,
const struct vb2_private_key *key)
{
/* Convert back to legacy vb1 algorithm enum */
uint64_t alg = vb2_get_crypto_algorithm(key->hash_alg, key->sig_alg);
if (alg == VB2_ALG_COUNT) {
fprintf(stderr, "Can't find crypto algorithm\n");
return VB2_ERROR_VB1_CRYPTO_ALGORITHM;
}
uint8_t *outbuf = NULL;
int buflen = i2d_RSAPrivateKey(key->rsa_private_key, &outbuf);
if (buflen <= 0) {
fprintf(stderr, "Unable to write private key buffer\n");
return VB2_ERROR_PRIVATE_KEY_WRITE_RSA;
}
FILE *f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Unable to open file %s\n", filename);
free(outbuf);
return VB2_ERROR_PRIVATE_KEY_WRITE_FILE;
}
if (1 != fwrite(&alg, sizeof(alg), 1, f) ||
1 != fwrite(outbuf, buflen, 1, f)) {
fprintf(stderr, "Unable to write to file %s\n", filename);
fclose(f);
unlink(filename); /* Delete any partial file */
free(outbuf);
return VB2_ERROR_PRIVATE_KEY_WRITE_FILE;
}
fclose(f);
free(outbuf);
return VB2_SUCCESS;
}
void vb2_init_packed_key(struct vb2_packed_key *key, uint8_t *key_data,
uint32_t key_size)
{
memset(key, 0, sizeof(*key));
key->key_offset = vb2_offset_of(key, key_data);
key->key_size = key_size;
key->algorithm = VB2_ALG_COUNT; /* Key not present yet */
}
struct vb2_packed_key *vb2_alloc_packed_key(uint32_t key_size,
uint32_t algorithm,
uint32_t version)
{
struct vb2_packed_key *key =
(struct vb2_packed_key *)calloc(sizeof(*key) + key_size, 1);
if (!key)
return NULL;
key->algorithm = algorithm;
key->key_version = version;
key->key_size = key_size;
key->key_offset = sizeof(*key);
return key;
}
vb2_error_t vb2_copy_packed_key(struct vb2_packed_key *dest,
const struct vb2_packed_key *src)
{
if (dest->key_size < src->key_size)
return VB2_ERROR_COPY_KEY_SIZE;
dest->key_size = src->key_size;
dest->algorithm = src->algorithm;
dest->key_version = src->key_version;
memcpy(vb2_packed_key_data_mutable(dest),
vb2_packed_key_data(src),
src->key_size);
return VB2_SUCCESS;
}
struct vb2_packed_key *vb2_read_packed_key(const char *filename)
{
struct vb2_packed_key *key = NULL;
uint32_t file_size = 0;
if (VB2_SUCCESS !=
vb2_read_file(filename, (uint8_t **)&key, &file_size)) {
return NULL;
}
if (vb2_packed_key_looks_ok(key, file_size) == VB2_SUCCESS)
return key;
/* Error */
free(key);
return NULL;
}
struct vb2_packed_key *vb2_read_packed_keyb(const char *filename,
uint32_t algorithm,
uint32_t version)
{
if (algorithm >= VB2_ALG_COUNT) {
fprintf(stderr, "%s() - invalid algorithm\n", __func__);
return NULL;
}
if (version > VB2_MAX_KEY_VERSION) {
/* Currently, TPM only supports 16-bit version */
fprintf(stderr, "%s() - invalid version %#x\n", __func__,
version);
return NULL;
}
uint8_t *key_data = NULL;
uint32_t key_size = 0;
if (VB2_SUCCESS != vb2_read_file(filename, &key_data, &key_size))
return NULL;
uint32_t expected_key_size =
vb2_packed_key_size(vb2_crypto_to_signature(algorithm));
if (!expected_key_size || expected_key_size != key_size) {
fprintf(stderr, "%s() - wrong key size %u for algorithm %u\n",
__func__, key_size, algorithm);
free(key_data);
return NULL;
}
struct vb2_packed_key *key =
vb2_alloc_packed_key(key_size, algorithm, version);
if (!key) {
free(key_data);
return NULL;
}
memcpy(vb2_packed_key_data_mutable(key), key_data, key_size);
free(key_data);
return key;
}
vb2_error_t vb2_write_packed_key(const char *filename,
const struct vb2_packed_key *key)
{
/* Copy the key, so its data is contiguous with the header */
struct vb2_packed_key *kcopy =
vb2_alloc_packed_key(key->key_size, 0, 0);
if (!kcopy)
return VB2_ERROR_PACKED_KEY_ALLOC;
if (VB2_SUCCESS != vb2_copy_packed_key(kcopy, key)) {
free(kcopy);
return VB2_ERROR_PACKED_KEY_COPY;
}
/* Write the copy, then free it */
vb2_error_t rv = vb2_write_file(filename, kcopy,
kcopy->key_offset + kcopy->key_size);
free(kcopy);
return rv;
}
vb2_error_t vb2_packed_key_looks_ok(const struct vb2_packed_key *key,
uint32_t size)
{
struct vb2_public_key pubkey;
vb2_error_t rv;
rv = vb2_unpack_key_buffer(&pubkey, (const uint8_t *)key, size);
if (rv)
return rv;
if (key->key_version > VB2_MAX_KEY_VERSION) {
/* Currently, TPM only supports 16-bit version */
VB2_DEBUG("packed key invalid version\n");
return VB2_ERROR_PACKED_KEY_VERSION;
}
return VB2_SUCCESS;
}
vb2_error_t vb2_unpack_key_data(struct vb2_public_key *key,
const uint8_t *key_data, uint32_t key_size)
{
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];
/* Validity 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;
}