| /* 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" |
| |
| /* 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; |
| } |