/* Copyright (c) 2011 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.
*/
/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
#include <openssl/pem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "cryptolib.h"
#include "host_common.h"
#include "host_key.h"
#include "host_misc.h"
#include "vboot_common.h"
VbPrivateKey* PrivateKeyReadPem(const char* filename, uint64_t algorithm) {
VbPrivateKey* key;
RSA* rsa_key;
FILE* f;
if (algorithm >= kNumAlgorithms) {
VBDEBUG(("%s() called with invalid algorithm!\n", __FUNCTION__));
return NULL;
}
/* Read private key */
f = fopen(filename, "r");
if (!f) {
VBDEBUG(("%s(): Couldn't open key file: %s\n", __FUNCTION__, filename));
return NULL;
}
rsa_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL);
fclose(f);
if (!rsa_key) {
VBDEBUG(("%s(): Couldn't read private key from file: %s\n", __FUNCTION__,
filename));
return NULL;
}
/* Store key and algorithm in our struct */
key = (VbPrivateKey*)malloc(sizeof(VbPrivateKey));
if (!key) {
RSA_free(rsa_key);
return NULL;
}
key->rsa_private_key = rsa_key;
key->algorithm = algorithm;
/* Return the key */
return key;
}
void PrivateKeyFree(VbPrivateKey* key) {
if (!key)
return;
if (key->rsa_private_key)
RSA_free(key->rsa_private_key);
free(key);
}
/* Write a private key to a file in .vbprivk format. */
int PrivateKeyWrite(const char* filename, const VbPrivateKey* key) {
uint8_t *outbuf = 0;
int buflen;
FILE *f;
buflen = i2d_RSAPrivateKey(key->rsa_private_key, &outbuf);
if (buflen <= 0) {
VbExError("Unable to write private key buffer\n");
return 1;
}
f = fopen(filename, "wb");
if (!f) {
VbExError("Unable to open file %s\n", filename);
free(outbuf);
return 1;
}
if (1 != fwrite(&key->algorithm, sizeof(key->algorithm), 1, f)) {
VbExError("Unable to write to file %s\n", filename);
fclose(f);
free(outbuf);
unlink(filename); /* Delete any partial file */
}
if (1 != fwrite(outbuf, buflen, 1, f)) {
VbExError("Unable to write to file %s\n", filename);
fclose(f);
unlink(filename); /* Delete any partial file */
free(outbuf);
}
fclose(f);
free(outbuf);
return 0;
}
VbPrivateKey* PrivateKeyRead(const char* filename) {
VbPrivateKey *key;
uint64_t filelen = 0;
uint8_t *buffer;
const unsigned char *start;
buffer = ReadFile(filename, &filelen);
if (!buffer) {
VbExError("unable to read from file %s\n", filename);
return 0;
}
key = (VbPrivateKey*)malloc(sizeof(VbPrivateKey));
if (!key) {
VbExError("Unable to allocate VbPrivateKey\n");
free(buffer);
return 0;
}
key->algorithm = *(typeof(key->algorithm) *)buffer;
start = buffer + sizeof(key->algorithm);
key->rsa_private_key = d2i_RSAPrivateKey(0, &start,
filelen - sizeof(key->algorithm));
if (!key->rsa_private_key) {
VbExError("Unable to parse RSA private key\n");
free(buffer);
free(key);
return 0;
}
free(buffer);
return key;
}
/* Allocate a new public key with space for a [key_size] byte key. */
VbPublicKey* PublicKeyAlloc(uint64_t key_size, uint64_t algorithm,
uint64_t version) {
VbPublicKey* key = (VbPublicKey*)malloc(sizeof(VbPublicKey) + key_size);
if (!key)
return NULL;
key->algorithm = algorithm;
key->key_version = version;
key->key_size = key_size;
key->key_offset = sizeof(VbPublicKey);
return key;
}
VbPublicKey* PublicKeyReadKeyb(const char* filename, uint64_t algorithm,
uint64_t version) {
VbPublicKey* key;
uint8_t* key_data;
uint64_t key_size;
uint64_t expected_key_size;
if (algorithm >= kNumAlgorithms) {
VBDEBUG(("PublicKeyReadKeyb() called with invalid algorithm!\n"));
return NULL;
}
if (version > 0xFFFF) {
/* Currently, TPM only supports 16-bit version */
VBDEBUG(("PublicKeyReadKeyb() called with invalid version!\n"));
return NULL;
}
key_data = ReadFile(filename, &key_size);
if (!key_data)
return NULL;
if (!RSAProcessedKeySize(algorithm, &expected_key_size) ||
expected_key_size != key_size) {
VBDEBUG(("PublicKeyReadKeyb() wrong key size for algorithm\n"));
free(key_data);
return NULL;
}
key = PublicKeyAlloc(key_size, algorithm, version);
if (!key) {
free(key_data);
return NULL;
}
Memcpy(GetPublicKeyData(key), key_data, key_size);
free(key_data);
return key;
}
int PublicKeyLooksOkay(VbPublicKey *key, uint64_t file_size)
{
uint64_t key_size;
/* Sanity-check key data */
if (0 != VerifyPublicKeyInside(key, file_size, key)) {
VBDEBUG(("PublicKeyRead() not a VbPublicKey\n"));
return 0;
}
if (key->algorithm >= kNumAlgorithms) {
VBDEBUG(("PublicKeyRead() invalid algorithm\n"));
return 0;
}
if (key->key_version > 0xFFFF) {
VBDEBUG(("PublicKeyRead() invalid version\n"));
return 0; /* Currently, TPM only supports 16-bit version */
}
if (!RSAProcessedKeySize(key->algorithm, &key_size) ||
key_size != key->key_size) {
VBDEBUG(("PublicKeyRead() wrong key size for algorithm\n"));
return 0;
}
/* Success */
return 1;
}
VbPublicKey* PublicKeyRead(const char* filename) {
VbPublicKey* key;
uint64_t file_size;
key = (VbPublicKey*)ReadFile(filename, &file_size);
if (!key)
return NULL;
if (PublicKeyLooksOkay(key, file_size))
return key;
/* Error */
free(key);
return NULL;
}
int PublicKeyWrite(const char* filename, const VbPublicKey* key) {
VbPublicKey* kcopy;
int rv;
/* Copy the key, so its data is contiguous with the header */
kcopy = PublicKeyAlloc(key->key_size, 0, 0);
if (!kcopy)
return 1;
if (0 != PublicKeyCopy(kcopy, key)) {
free(kcopy);
return 1;
}
/* Write the copy, then free it */
rv = WriteFile(filename, kcopy, kcopy->key_offset + kcopy->key_size);
free(kcopy);
return rv;
}