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