| /* Copyright 2013-2016 IBM Corp. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <config.h> |
| |
| #include <stdbool.h> |
| #include <types.h> |
| #include "container.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <getopt.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #include <fcntl.h> |
| #include <assert.h> |
| #include <sysexits.h> |
| |
| #include <openssl/ec.h> |
| #include <openssl/ecdsa.h> |
| #include <openssl/pem.h> |
| #include <openssl/sha.h> |
| |
| #define PREFIX_HDR 0 |
| #define SOFTWARE_HDR 1 |
| |
| char *progname; |
| int debug; |
| |
| void usage(int status); |
| |
| void getPublicKeyRaw(ecc_key_t *pubkeyraw, char *inFile) |
| { |
| EVP_PKEY* pkey; |
| EC_KEY *key; |
| const EC_GROUP *ecgrp; |
| const EC_POINT *ecpoint; |
| BIGNUM *pubkeyBN; |
| unsigned char pubkeyData[1 + 2*EC_COORDBYTES]; |
| |
| FILE *fp = fopen( inFile, "r"); |
| pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL); |
| assert(pkey); |
| |
| key = EVP_PKEY_get1_EC_KEY(pkey); |
| assert(key); |
| ecgrp = EC_KEY_get0_group(key); |
| assert(ecgrp); |
| ecpoint = EC_KEY_get0_public_key(key); |
| assert(ecpoint); |
| pubkeyBN = EC_POINT_point2bn(ecgrp, ecpoint, POINT_CONVERSION_UNCOMPRESSED, NULL, NULL); |
| BN_bn2bin(pubkeyBN, pubkeyData); |
| |
| if (debug) |
| printBytes((char *)"pubkey (RAW) = ", &pubkeyData[1], sizeof(pubkeyData) - 1, 32); |
| |
| memcpy(*pubkeyraw, &pubkeyData[1], sizeof(ecc_key_t)); |
| |
| EC_KEY_free(key); |
| EVP_PKEY_free(pkey); |
| fclose(fp); |
| |
| return; |
| } |
| |
| void getSigRaw(ecc_signature_t *sigraw, char *inFile) |
| { |
| ECDSA_SIG* signature; |
| int fdin; |
| struct stat s; |
| void *infile; |
| unsigned char outbuf[2*EC_COORDBYTES]; |
| int r, rlen, roff, slen, soff; |
| |
| fdin = open(inFile, O_RDONLY); |
| assert(fdin > 0); |
| r = fstat(fdin, &s); |
| assert(r==0); |
| |
| infile = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fdin, 0); |
| assert(infile); |
| |
| signature = d2i_ECDSA_SIG(NULL, (const unsigned char **) &infile, 7 + 2*EC_COORDBYTES); |
| |
| memset(&outbuf, 0, sizeof(outbuf)); |
| |
| rlen = BN_num_bytes(signature->r); |
| roff = 66 - rlen; |
| BN_bn2bin(signature->r, &outbuf[roff]); |
| |
| slen = BN_num_bytes(signature->s); |
| soff = 66 + (66 - slen); |
| BN_bn2bin(signature->s, &outbuf[soff]); |
| |
| if (debug) |
| printBytes((char *)"sig (RAW) = ", outbuf, sizeof(outbuf), 32); |
| |
| memcpy(*sigraw, outbuf, 2*EC_COORDBYTES); |
| |
| ECDSA_SIG_free(signature); |
| close(fdin); |
| |
| return; |
| } |
| |
| void writeHdr(void *hdr, const char *outFile, int hdr_type) |
| { |
| int fdout; |
| int r, hdr_sz=0; |
| const char *lead; |
| unsigned char md[SHA512_DIGEST_LENGTH]; |
| |
| fdout = open(outFile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| assert(fdout > 0); |
| |
| switch (hdr_type) { |
| case PREFIX_HDR: |
| hdr_sz = sizeof(ROM_prefix_header_raw); |
| break; |
| case SOFTWARE_HDR: |
| hdr_sz = sizeof(ROM_sw_header_raw); |
| break; |
| } |
| |
| r = write(fdout, (const void *)hdr, hdr_sz); |
| assert(r > 0); |
| |
| if (debug) { |
| if (hdr_type == PREFIX_HDR) |
| lead = "PR hdr hash = "; |
| else |
| lead = "SW hdr hash = "; |
| |
| SHA512(hdr, r, md); |
| printBytes((char *)lead, md, sizeof(md), 32); |
| } |
| |
| close(fdout); |
| |
| return; |
| } |
| |
| void printBytes(char *lead, unsigned char *buffer, size_t buflen, int wrap) |
| { |
| int i; |
| int leadbytes = strlen(lead); |
| leadbytes = leadbytes > 30 ? 30 : leadbytes; |
| |
| fprintf (stderr, "%s", lead); |
| for (i = 1; i < buflen + 1; i++) { |
| fprintf (stderr, "%02x", buffer[i - 1]); |
| if (((i % wrap) == 0) && (i < buflen)) |
| fprintf (stderr, "\n%*c", leadbytes, ' '); |
| } |
| fprintf (stderr, "\n"); |
| } |
| |
| __attribute__((__noreturn__)) void usage (int status) |
| { |
| if (status != 0) { |
| fprintf(stderr, "Try '%s --help' for more information.\n", progname); |
| } |
| else { |
| printf("Usage: %s [options]\n", progname); |
| printf( |
| "\n" |
| "Options:\n" |
| " -d, --debug show additional debug info\n" |
| " -h, --help display this message and exit\n" |
| " -a, --hw_key_a file containing HW key A private key in PEM format\n" |
| " -b, --hw_key_b file containing HW key B private key in PEM format\n" |
| " -c, --hw_key_c file containing HW key C private key in PEM format\n" |
| " -p, --sw_key_p file containing SW key P private key in PEM format\n" |
| " -q, --sw_key_q file containing SW key Q private key in PEM format\n" |
| " -r, --sw_key_r file containing SW key R private key in PEM format\n" |
| " -A, --hw_sig_a file containing HW key A signature in DER format\n" |
| " -B, --hw_sig_b file containing HW key B signature in DER format\n" |
| " -C, --hw_sig_c file containing HW key C signature in DER format\n" |
| " -P, --sw_sig_p file containing SW key P signature in DER format\n" |
| " -Q, --sw_sig_q file containing SW key Q signature in DER format\n" |
| " -R, --sw_sig_r file containing SW key R signature in DER format\n" |
| " -L, --payload file containing the payload to be signed\n" |
| " -I, --imagefile file to write containerized payload (output)\n" |
| " --dumpPrefixHdr file to dump Prefix header blob (to be signed)\n" |
| " --dumpSwHdr file to dump Software header blob (to be signed)\n" |
| "\n"); |
| }; |
| exit(status); |
| } |
| |
| static struct option const opts[] = { |
| { "debug", no_argument, 0, 'd' }, |
| { "help", no_argument, 0, 'h' }, |
| { "hw_key_a", required_argument, 0, 'a' }, |
| { "hw_key_b", required_argument, 0, 'b' }, |
| { "hw_key_c", required_argument, 0, 'c' }, |
| { "sw_key_p", required_argument, 0, 'p' }, |
| { "sw_key_q", required_argument, 0, 'q' }, |
| { "sw_key_r", required_argument, 0, 'r' }, |
| { "hw_sig_a", required_argument, 0, 'A' }, |
| { "hw_sig_b", required_argument, 0, 'B' }, |
| { "hw_sig_c", required_argument, 0, 'C' }, |
| { "sw_sig_p", required_argument, 0, 'P' }, |
| { "sw_sig_q", required_argument, 0, 'Q' }, |
| { "sw_sig_r", required_argument, 0, 'R' }, |
| { "payload", required_argument, 0, 'L' }, |
| { "imagefile", required_argument, 0, 'I' }, |
| { "dumpPrefixHdr", required_argument, 0, 128 }, |
| { "dumpSwHdr", required_argument, 0, 129 }, |
| {NULL, 0, 0, 0} |
| }; |
| |
| static struct { |
| char *hw_keyfn_a; |
| char *hw_keyfn_b; |
| char *hw_keyfn_c; |
| char *sw_keyfn_p; |
| char *sw_keyfn_q; |
| char *sw_keyfn_r; |
| char *hw_sigfn_a; |
| char *hw_sigfn_b; |
| char *hw_sigfn_c; |
| char *sw_sigfn_p; |
| char *sw_sigfn_q; |
| char *sw_sigfn_r; |
| char *imagefn; |
| char *payloadfn; |
| char *prhdrfn; |
| char *swhdrfn; |
| } params; |
| |
| |
| int main(int argc, char* argv[]) |
| { |
| int fdin, fdout; |
| void *container = malloc(SECURE_BOOT_HEADERS_SIZE); |
| struct stat s; |
| char *buf = malloc(4096); |
| off_t l; |
| void *infile; |
| int r; |
| ROM_container_raw *c = (ROM_container_raw*)container; |
| ROM_prefix_header_raw *ph; |
| ROM_prefix_data_raw *pd; |
| ROM_sw_header_raw *swh; |
| ROM_sw_sig_raw *ssig; |
| |
| unsigned char md[SHA512_DIGEST_LENGTH]; |
| void *p; |
| ecc_key_t pubkeyraw; |
| ecc_signature_t sigraw; |
| int indexptr; |
| |
| progname = strrchr (argv[0], '/'); |
| if (progname != NULL) |
| ++progname; |
| else |
| progname = argv[0]; |
| |
| memset(container, 0, SECURE_BOOT_HEADERS_SIZE); |
| |
| while (1) { |
| int opt; |
| opt = getopt_long(argc, argv, "a:b:c:p:q:r:A:B:C:P:Q:R:L:I:dh", opts, &indexptr); |
| if (opt == -1) |
| break; |
| |
| switch (opt) { |
| case 'h': |
| case '?': |
| usage(EX_OK); |
| break; |
| case 'd': |
| debug = 1; |
| break; |
| case 'a': |
| params.hw_keyfn_a = optarg; |
| break; |
| case 'b': |
| params.hw_keyfn_b = optarg; |
| break; |
| case 'c': |
| params.hw_keyfn_c = optarg; |
| break; |
| case 'p': |
| params.sw_keyfn_p = optarg; |
| break; |
| case 'q': |
| params.sw_keyfn_q = optarg; |
| break; |
| case 'r': |
| params.sw_keyfn_r = optarg; |
| break; |
| case 'A': |
| params.hw_sigfn_a = optarg; |
| break; |
| case 'B': |
| params.hw_sigfn_b = optarg; |
| break; |
| case 'C': |
| params.hw_sigfn_c = optarg; |
| break; |
| case 'P': |
| params.sw_sigfn_p = optarg; |
| break; |
| case 'Q': |
| params.sw_sigfn_q = optarg; |
| break; |
| case 'R': |
| params.sw_sigfn_r = optarg; |
| break; |
| case 'L': |
| params.payloadfn = optarg; |
| break; |
| case 'I': |
| params.imagefn = optarg; |
| break; |
| case 128: |
| params.prhdrfn = optarg; |
| break; |
| case 129: |
| params.swhdrfn = optarg; |
| break; |
| default: |
| usage(EX_USAGE); |
| } |
| } |
| // } |
| |
| fdin = open(params.payloadfn, O_RDONLY); |
| assert(fdin > 0); |
| r = fstat(fdin, &s); |
| assert(r==0); |
| infile = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fdin, 0); |
| assert(infile); |
| fdout = open(params.imagefn, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| assert(fdout > 0); |
| |
| c->magic_number = cpu_to_be32(ROM_MAGIC_NUMBER); |
| c->version = cpu_to_be16(1); |
| c->container_size = cpu_to_be64(SECURE_BOOT_HEADERS_SIZE + s.st_size); |
| c->target_hrmor = 0; |
| c->stack_pointer = 0; |
| memset(c->hw_pkey_a, 0, sizeof(ecc_key_t)); |
| memset(c->hw_pkey_b, 0, sizeof(ecc_key_t)); |
| memset(c->hw_pkey_c, 0, sizeof(ecc_key_t)); |
| if (params.hw_keyfn_a) { |
| getPublicKeyRaw(&pubkeyraw, params.hw_keyfn_a); |
| memcpy(c->hw_pkey_a, pubkeyraw, sizeof(ecc_key_t)); |
| } |
| if (params.hw_keyfn_b) { |
| getPublicKeyRaw(&pubkeyraw, params.hw_keyfn_b); |
| memcpy(c->hw_pkey_b, pubkeyraw, sizeof(ecc_key_t)); |
| } |
| if (params.hw_keyfn_c) { |
| getPublicKeyRaw(&pubkeyraw, params.hw_keyfn_c); |
| memcpy(c->hw_pkey_c, pubkeyraw, sizeof(ecc_key_t)); |
| } |
| |
| ph = container + sizeof(ROM_container_raw); |
| ph->ver_alg.version = cpu_to_be16(1); |
| ph->ver_alg.hash_alg = 1; |
| ph->ver_alg.sig_alg = 1; |
| ph->code_start_offset = 0; |
| ph->reserved = 0; |
| ph->flags = cpu_to_be32(0x80000000); |
| memset(ph->payload_hash, 0, sizeof(sha2_hash_t)); |
| ph->ecid_count = 0; |
| |
| pd = (ROM_prefix_data_raw*)ph->ecid; |
| memset(pd->hw_sig_a, 0, sizeof(ecc_signature_t)); |
| memset(pd->hw_sig_b, 0, sizeof(ecc_signature_t)); |
| memset(pd->hw_sig_c, 0, sizeof(ecc_signature_t)); |
| if (params.hw_sigfn_a) { |
| getSigRaw(&sigraw, params.hw_sigfn_a); |
| memcpy(pd->hw_sig_a, sigraw, sizeof(ecc_key_t)); |
| } |
| if (params.hw_sigfn_b) { |
| getSigRaw(&sigraw, params.hw_sigfn_b); |
| memcpy(pd->hw_sig_b, sigraw, sizeof(ecc_key_t)); |
| } |
| if (params.hw_sigfn_c) { |
| getSigRaw(&sigraw, params.hw_sigfn_c); |
| memcpy(pd->hw_sig_c, sigraw, sizeof(ecc_key_t)); |
| } |
| memset(pd->sw_pkey_p, 0, sizeof(ecc_key_t)); |
| memset(pd->sw_pkey_q, 0, sizeof(ecc_key_t)); |
| memset(pd->sw_pkey_r, 0, sizeof(ecc_key_t)); |
| if (params.sw_keyfn_p) { |
| getPublicKeyRaw(&pubkeyraw, params.sw_keyfn_p); |
| memcpy(pd->sw_pkey_p, pubkeyraw, sizeof(ecc_key_t)); |
| ph->sw_key_count++; |
| } |
| if (params.sw_keyfn_q) { |
| getPublicKeyRaw(&pubkeyraw, params.sw_keyfn_q); |
| memcpy(pd->sw_pkey_q, pubkeyraw, sizeof(ecc_key_t)); |
| ph->sw_key_count++; |
| } |
| if (params.sw_keyfn_r) { |
| getPublicKeyRaw(&pubkeyraw, params.sw_keyfn_r); |
| memcpy(pd->sw_pkey_r, pubkeyraw, sizeof(ecc_key_t)); |
| ph->sw_key_count++; |
| } |
| ph->payload_size = cpu_to_be64(ph->sw_key_count * sizeof(ecc_key_t)); |
| p = SHA512(pd->sw_pkey_p, sizeof(ecc_key_t) * ph->sw_key_count, md); |
| assert(p); |
| memcpy(ph->payload_hash, md, sizeof(sha2_hash_t)); |
| |
| if (params.prhdrfn) |
| writeHdr((void *)ph, params.prhdrfn, PREFIX_HDR); |
| |
| swh = (ROM_sw_header_raw*)(((uint8_t*)pd) + sizeof(ecc_signature_t)*3 + be64_to_cpu(ph->payload_size)); |
| swh->ver_alg.version = cpu_to_be16(1); |
| swh->ver_alg.hash_alg = 1; |
| swh->ver_alg.sig_alg = 1; |
| swh->code_start_offset = 0; |
| swh->reserved = 0; |
| swh->flags = 0; |
| swh->reserved_0 = 0; |
| swh->payload_size = cpu_to_be64(s.st_size); |
| p = SHA512(infile, s.st_size, md); |
| assert(p); |
| memcpy(swh->payload_hash, md, sizeof(sha2_hash_t)); |
| |
| if (params.swhdrfn) |
| writeHdr((void *)swh, params.swhdrfn, SOFTWARE_HDR); |
| |
| ssig = (ROM_sw_sig_raw*)(((uint8_t*)swh) + sizeof(ROM_sw_header_raw)); |
| memset(ssig->sw_sig_p, 0, sizeof(ecc_signature_t)); |
| memset(ssig->sw_sig_q, 0, sizeof(ecc_signature_t)); |
| memset(ssig->sw_sig_r, 0, sizeof(ecc_signature_t)); |
| if (params.sw_sigfn_p) { |
| getSigRaw(&sigraw, params.sw_sigfn_p); |
| memcpy(ssig->sw_sig_p, sigraw, sizeof(ecc_key_t)); |
| } |
| if (params.sw_sigfn_q) { |
| getSigRaw(&sigraw, params.sw_sigfn_q); |
| memcpy(ssig->sw_sig_q, sigraw, sizeof(ecc_key_t)); |
| } |
| if (params.sw_sigfn_r) { |
| getSigRaw(&sigraw, params.sw_sigfn_r); |
| memcpy(ssig->sw_sig_r, sigraw, sizeof(ecc_key_t)); |
| } |
| |
| r = write(fdout, container, SECURE_BOOT_HEADERS_SIZE); |
| assert(r == 4096); |
| read(fdin, buf, s.st_size%4096); |
| write(fdout, buf, s.st_size%4096); |
| l = s.st_size - s.st_size%4096; |
| while (l) { |
| read(fdin, buf, 4096); |
| write(fdout, buf, 4096); |
| l-=4096; |
| }; |
| close(fdin); |
| close(fdout); |
| |
| free(container); |
| free(buf); |
| return 0; |
| } |