blob: db536c6eb397cb858e6787c495db6c157c661ee1 [file] [log] [blame]
/* 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 signature generation.
*/
/* TODO: change all 'return 0', 'return 1' into meaningful return codes */
#include <openssl/rsa.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "2common.h"
#include "2rsa.h"
#include "2sha.h"
#include "2sysincludes.h"
#include "host_common.h"
#include "host_signature21.h"
#include "vb2_common.h"
/* Invoke [external_signer] command with [pem_file] as an argument, contents of
* [inbuf] passed redirected to stdin, and the stdout of the command is put
* back into [outbuf]. Returns -1 on error, 0 on success.
*/
static int sign_external(uint32_t size, const uint8_t *inbuf, uint8_t *outbuf,
uint32_t outbufsize, const char *pem_file,
const char *external_signer)
{
int rv = 0, n;
int p_to_c[2], c_to_p[2]; /* pipe descriptors */
pid_t pid;
VB2_DEBUG("Will invoke \"%s %s\" to perform signing.\n"
"Input to the signer will be provided on standard in.\n"
"Output of the signer will be read from standard out.\n",
external_signer, pem_file);
/* Need two pipes since we want to invoke the external_signer as
* a co-process writing to its stdin and reading from its stdout. */
if (pipe(p_to_c) < 0 || pipe(c_to_p) < 0) {
VB2_DEBUG("pipe() error\n");
return -1;
}
if ((pid = fork()) < 0) {
VB2_DEBUG("fork() error\n");
return -1;
} else if (pid > 0) { /* Parent. */
close(p_to_c[STDIN_FILENO]);
close(c_to_p[STDOUT_FILENO]);
/* We provide input to the child process (external signer). */
if (write(p_to_c[STDOUT_FILENO], inbuf, size) != size) {
VB2_DEBUG("write() error\n");
rv = -1;
} else {
/* Send EOF to child (signer process). */
close(p_to_c[STDOUT_FILENO]);
do {
n = read(c_to_p[STDIN_FILENO], outbuf,
outbufsize);
outbuf += n;
outbufsize -= n;
} while (n > 0 && outbufsize);
if (n < 0) {
VB2_DEBUG("read() error\n");
rv = -1;
}
}
if (waitpid(pid, NULL, 0) < 0) {
VB2_DEBUG("waitpid() error\n");
rv = -1;
}
} else { /* Child. */
close (p_to_c[STDOUT_FILENO]);
close (c_to_p[STDIN_FILENO]);
/* Map the stdin to the first pipe (this pipe gets input
* from the parent) */
if (STDIN_FILENO != p_to_c[STDIN_FILENO]) {
if (dup2(p_to_c[STDIN_FILENO], STDIN_FILENO) !=
STDIN_FILENO) {
VB2_DEBUG("stdin dup2() failed\n");
close(p_to_c[0]);
return -1;
}
}
/* Map the stdout to the second pipe (this pipe sends back
* signer output to the parent) */
if (STDOUT_FILENO != c_to_p[STDOUT_FILENO]) {
if (dup2(c_to_p[STDOUT_FILENO], STDOUT_FILENO) !=
STDOUT_FILENO) {
VB2_DEBUG("stdout dup2() failed\n");
close(c_to_p[STDOUT_FILENO]);
return -1;
}
}
/* External signer is invoked here. */
if (execl(external_signer, external_signer, pem_file,
(char *) 0) < 0) {
VB2_DEBUG("execl() of external signer failed\n");
}
}
return rv;
}
struct vb2_signature *vb2_external_signature(const uint8_t *data, uint32_t size,
const char *key_file,
uint32_t key_algorithm,
const char *external_signer)
{
int vb2_alg = vb2_crypto_to_hash(key_algorithm);
uint8_t digest[VB2_MAX_DIGEST_SIZE];
int digest_size = vb2_digest_size(vb2_alg);
uint32_t digest_info_size = 0;
const uint8_t *digest_info = NULL;
if (VB2_SUCCESS != vb2_digest_info(vb2_alg,
&digest_info, &digest_info_size))
return NULL;
uint8_t *signature_digest;
uint64_t signature_digest_len = digest_size + digest_info_size;
int rv;
/* Calculate the digest */
if (VB2_SUCCESS != vb2_digest_buffer(data, size, vb2_alg,
digest, sizeof(digest)))
return NULL;
/* Prepend the digest info to the digest */
signature_digest = calloc(signature_digest_len, 1);
if (!signature_digest)
return NULL;
memcpy(signature_digest, digest_info, digest_info_size);
memcpy(signature_digest + digest_info_size, digest, digest_size);
/* Allocate output signature */
uint32_t sig_size =
vb2_rsa_sig_size(vb2_crypto_to_signature(key_algorithm));
struct vb2_signature *sig = vb2_alloc_signature(sig_size, size);
if (!sig) {
free(signature_digest);
return NULL;
}
/* Sign the signature_digest into our output buffer */
rv = sign_external(signature_digest_len, /* Input length */
signature_digest, /* Input data */
vb2_signature_data_mutable(sig), /* Output sig */
sig_size, /* Max Output sig size */
key_file, /* Key file to use */
external_signer); /* External cmd to invoke */
free(signature_digest);
if (-1 == rv) {
VB2_DEBUG("RSA_private_encrypt() failed.\n");
free(sig);
return NULL;
}
/* Return the signature */
return sig;
}