| /* 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 "cryptolib.h" |
| #include "file_keys.h" |
| #include "host_common.h" |
| #include "vboot_common.h" |
| |
| |
| VbSignature* SignatureAlloc(uint64_t sig_size, uint64_t data_size) { |
| VbSignature* sig = (VbSignature*)malloc(sizeof(VbSignature) + sig_size); |
| if (!sig) |
| return NULL; |
| |
| sig->sig_offset = sizeof(VbSignature); |
| sig->sig_size = sig_size; |
| sig->data_size = data_size; |
| return sig; |
| } |
| |
| |
| void SignatureInit(VbSignature* sig, uint8_t* sig_data, |
| uint64_t sig_size, uint64_t data_size) { |
| sig->sig_offset = OffsetOf(sig, sig_data); |
| sig->sig_size = sig_size; |
| sig->data_size = data_size; |
| } |
| |
| |
| int SignatureCopy(VbSignature* dest, const VbSignature* src) { |
| if (dest->sig_size < src->sig_size) |
| return 1; |
| dest->sig_size = src->sig_size; |
| dest->data_size = src->data_size; |
| Memcpy(GetSignatureData(dest), GetSignatureDataC(src), src->sig_size); |
| return 0; |
| } |
| |
| |
| VbSignature* CalculateChecksum(const uint8_t* data, uint64_t size) { |
| |
| uint8_t* header_checksum; |
| VbSignature* sig; |
| |
| header_checksum = DigestBuf(data, size, SHA512_DIGEST_ALGORITHM); |
| if (!header_checksum) |
| return NULL; |
| |
| sig = SignatureAlloc(SHA512_DIGEST_SIZE, 0); |
| if (!sig) { |
| VbExFree(header_checksum); |
| return NULL; |
| } |
| sig->sig_offset = sizeof(VbSignature); |
| sig->sig_size = SHA512_DIGEST_SIZE; |
| sig->data_size = size; |
| |
| /* Signature data immediately follows the header */ |
| Memcpy(GetSignatureData(sig), header_checksum, SHA512_DIGEST_SIZE); |
| VbExFree(header_checksum); |
| return sig; |
| } |
| |
| VbSignature* CalculateHash(const uint8_t* data, uint64_t size, |
| const VbPrivateKey* key) { |
| uint8_t* digest = NULL; |
| int digest_size = hash_size_map[key->algorithm]; |
| VbSignature* sig = NULL; |
| |
| /* Calculate the digest */ |
| digest = DigestBuf(data, size, key->algorithm); |
| if (!digest) |
| return NULL; |
| |
| /* Allocate output signature */ |
| sig = SignatureAlloc(digest_size, size); |
| if (!sig) { |
| free(digest); |
| return NULL; |
| } |
| |
| /* The digest itself is the signature data */ |
| Memcpy(GetSignatureData(sig), digest, digest_size); |
| free(digest); |
| |
| /* Return the signature */ |
| return sig; |
| } |
| |
| VbSignature* CalculateSignature(const uint8_t* data, uint64_t size, |
| const VbPrivateKey* key) { |
| |
| uint8_t* digest; |
| int digest_size = hash_size_map[key->algorithm]; |
| |
| const uint8_t* digestinfo = hash_digestinfo_map[key->algorithm]; |
| int digestinfo_size = digestinfo_size_map[key->algorithm]; |
| |
| uint8_t* signature_digest; |
| int signature_digest_len = digest_size + digestinfo_size; |
| |
| VbSignature* sig; |
| int rv; |
| |
| /* Calculate the digest */ |
| /* TODO: rename param 3 of DigestBuf to hash_type */ |
| digest = DigestBuf(data, size, hash_type_map[key->algorithm]); |
| if (!digest) |
| return NULL; |
| |
| /* Prepend the digest info to the digest */ |
| signature_digest = malloc(signature_digest_len); |
| if (!signature_digest) { |
| VbExFree(digest); |
| return NULL; |
| } |
| Memcpy(signature_digest, digestinfo, digestinfo_size); |
| Memcpy(signature_digest + digestinfo_size, digest, digest_size); |
| VbExFree(digest); |
| |
| /* Allocate output signature */ |
| sig = SignatureAlloc(siglen_map[key->algorithm], size); |
| if (!sig) { |
| free(signature_digest); |
| return NULL; |
| } |
| |
| /* Sign the signature_digest into our output buffer */ |
| rv = RSA_private_encrypt(signature_digest_len, /* Input length */ |
| signature_digest, /* Input data */ |
| GetSignatureData(sig), /* Output sig */ |
| key->rsa_private_key, /* Key to use */ |
| RSA_PKCS1_PADDING); /* Padding to use */ |
| free(signature_digest); |
| |
| if (-1 == rv) { |
| VBDEBUG(("SignatureBuf(): RSA_private_encrypt() failed.\n")); |
| free(sig); |
| return NULL; |
| } |
| |
| /* Return the signature */ |
| return sig; |
| } |
| |
| /* 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. |
| */ |
| int InvokeExternalSigner(uint64_t size, |
| const uint8_t* inbuf, |
| uint8_t* outbuf, |
| uint64_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; |
| |
| VBDEBUG(("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) { |
| VBDEBUG(("pipe() error\n")); |
| return -1; |
| } |
| if ((pid = fork()) < 0) { |
| VBDEBUG(("fork() error")); |
| 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) { |
| VBDEBUG(("write() error while providing input to external signer\n")); |
| rv = -1; |
| } else { |
| close(p_to_c[STDOUT_FILENO]); /* Send EOF to child (signer process). */ |
| do { |
| n = read(c_to_p[STDIN_FILENO], outbuf, outbufsize); |
| outbuf += n; |
| outbufsize -= n; |
| } while (n > 0 && outbufsize); |
| |
| if (n < 0) { |
| VBDEBUG(("read() error while reading output from external signer\n")); |
| rv = -1; |
| } |
| } |
| if (waitpid(pid, NULL, 0) < 0) { |
| VBDEBUG(("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) { |
| VBDEBUG(("stdin dup2() failed (external signer)\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) { |
| VBDEBUG(("stdout dup2() failed (external signer)\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) { |
| VBDEBUG(("execl() of external signer failed\n")); |
| } |
| } |
| return rv; |
| } |
| |
| /* TODO(gauravsh): This could easily be integrated into CalculateSignature() |
| * since the code is almost a mirror - I have kept it as such to avoid changing |
| * the existing interface. */ |
| VbSignature* CalculateSignature_external(const uint8_t* data, uint64_t size, |
| const char* key_file, |
| uint64_t key_algorithm, |
| const char* external_signer) { |
| uint8_t* digest; |
| uint64_t digest_size = hash_size_map[key_algorithm]; |
| |
| const uint8_t* digestinfo = hash_digestinfo_map[key_algorithm]; |
| uint64_t digestinfo_size = digestinfo_size_map[key_algorithm]; |
| |
| uint8_t* signature_digest; |
| uint64_t signature_digest_len = digest_size + digestinfo_size; |
| |
| VbSignature* sig; |
| int rv; |
| |
| /* Calculate the digest */ |
| /* TODO: rename param 3 of DigestBuf to hash_type */ |
| digest = DigestBuf(data, size, hash_type_map[key_algorithm]); |
| if (!digest) |
| return NULL; |
| |
| /* Prepend the digest info to the digest */ |
| signature_digest = malloc(signature_digest_len); |
| if (!signature_digest) { |
| free(digest); |
| return NULL; |
| } |
| Memcpy(signature_digest, digestinfo, digestinfo_size); |
| Memcpy(signature_digest + digestinfo_size, digest, digest_size); |
| free(digest); |
| |
| /* Allocate output signature */ |
| sig = SignatureAlloc(siglen_map[key_algorithm], size); |
| if (!sig) { |
| free(signature_digest); |
| return NULL; |
| } |
| |
| /* Sign the signature_digest into our output buffer */ |
| rv = InvokeExternalSigner(signature_digest_len, /* Input length */ |
| signature_digest, /* Input data */ |
| GetSignatureData(sig), /* Output sig */ |
| siglen_map[key_algorithm], /* Max Output sig size */ |
| key_file, /* Key file to use */ |
| external_signer); /* External cmd to invoke */ |
| free(signature_digest); |
| |
| if (-1 == rv) { |
| VBDEBUG(("SignatureBuf(): RSA_private_encrypt() failed.\n")); |
| free(sig); |
| return NULL; |
| } |
| |
| /* Return the signature */ |
| return sig; |
| } |