diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2015-08-21 16:57:06 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-08-25 20:10:55 +0000 |
commit | d9a614826b524c212b1ce449141a8af047178b38 (patch) | |
tree | 00e43f7828f374ad08f6aa6cd89619396e5abd49 /util | |
parent | b37514eed2873dcecc44619ea07542912aaf977b (diff) | |
download | chrome-ec-d9a614826b524c212b1ce449141a8af047178b38.tar.gz |
cr50: add code for the signer utility
This utility reads a binary file, verifies that the first 1024 bytes
of the file are set to zero and replaces this block with a header,
containing the signature and other information required by the recent
CR50 ROM.
A test private key is included, it matches the FPGA ROM public key.
The use convention is simple: two parameters are required, the private
key file name and the binary file name. The signed binary file is saved
in the file with extension ".signed".
BRANCH=none
BUG=chrome-os-partner:43025
TEST=the utility builds using
g++ -std=c++0x -I . -o signer codesigner.cc publickey.cc -lcrypto
ec.RO.flat signed with this utility can be successfully bootstrapped
a CR50 over SPS
Change-Id: I046b13d20f0dd8cff884e37ef966593e01dcb043
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/295208
Reviewed-by: Marius Schilder <mschilder@chromium.org>
Diffstat (limited to 'util')
-rw-r--r-- | util/signer/.gitignore | 1 | ||||
-rw-r--r-- | util/signer/codesigner.cc | 122 | ||||
-rw-r--r-- | util/signer/publickey.cc | 244 | ||||
-rw-r--r-- | util/signer/publickey.h | 52 | ||||
-rw-r--r-- | util/signer/rom-testkey.pem | 45 | ||||
-rw-r--r-- | util/signer/signed_header.h | 29 |
6 files changed, 493 insertions, 0 deletions
diff --git a/util/signer/.gitignore b/util/signer/.gitignore new file mode 100644 index 0000000000..6dcef0cd25 --- /dev/null +++ b/util/signer/.gitignore @@ -0,0 +1 @@ +signer diff --git a/util/signer/codesigner.cc b/util/signer/codesigner.cc new file mode 100644 index 0000000000..989e87b3c5 --- /dev/null +++ b/util/signer/codesigner.cc @@ -0,0 +1,122 @@ +// +// Copyright 2015 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. +// + +#include <iostream> +#include <fstream> +#include <sstream> +#include <ios> +#include <string> + + +#include <openssl/pem.h> + + + +#include <signed_header.h> +#include <publickey.h> + +using namespace std; +static uint8_t *mem; // this is where the file to sign is loaded +// Size of the file to be signed (including 1Kb signature). +static size_t flat_size; + + +int LoadFlatFile(const char *name, size_t header_size) +{ + ifstream::pos_type fileSize; + ifstream FlatFile (name, ios::in | ios::binary | ios::ate); + + if(!FlatFile.is_open()) { + fprintf(stderr, "failed to open %s\n", name); + return -1; + } + + flat_size = FlatFile.tellg(); + + mem = new uint8_t[flat_size]; + FlatFile.seekg(0, ios::beg); + if(!FlatFile.read((char *)mem, flat_size)) { + fprintf(stderr, "failed to read file %s\n", name); + return -1; + } + FlatFile.close(); + + // verify that there is enough room at the bottom + for (size_t i = 0; i < header_size; i++) + if (mem[i]) { + fprintf(stderr, "nonzero value at offset %zd\n", i); + return -1; + } + + return 0; +} + +int SaveSignedFile(const char *name) +{ + FILE* fp = fopen(name, "wb"); + + if (!fp) { + fprintf(stderr, "failed to open file '%s': %s\n", name, strerror(errno)); + return -1; + } + if (fwrite(mem, 1, flat_size, fp) != flat_size) { + fprintf(stderr, "failed to write %zd bytes to '%s': %s\n", + flat_size, name, strerror(errno)); + return -1; + } + fclose(fp); + + return 0; +} + +// Sing the previously read file. Return zero on success, nonzero on failure. +static int sign(PublicKey& key, const SignedHeader* input_hdr) { + BIGNUM* sig = NULL; + SignedHeader* hdr = (SignedHeader*)(&mem[0]); + int result; + + memcpy(hdr, input_hdr, sizeof(SignedHeader)); + + result = key.sign(&hdr->tag, flat_size - offsetof(SignedHeader, tag), &sig); + + if (result == 1) { + hdr->image_size = flat_size; + size_t nwords = key.nwords(); + key.toArray(hdr->signature, nwords, sig); + } else { + fprintf(stderr, "ossl_sign:%d\n", result); + } + + if (sig) + BN_free(sig); + + return result != 1; +} + +int main(int argc, char* argv[]) { + if (argc < 3) { + fprintf(stderr, "Usage: %s pem-file [hexfile|flatfile]\n", argv[0]); + exit(1); + } + const char* arg = argv[2]; + PublicKey key(argv[1]); + + if (!key.ok()) return -1; + + SignedHeader hdr; + + // Load input file + if (LoadFlatFile(arg, sizeof(hdr))) + return -2; + + if (sign(key, &hdr)) + return -3; + + if (SaveSignedFile((std::string(arg) + std::string(".signed")).c_str())) + return -4; + + return 0; +} diff --git a/util/signer/publickey.cc b/util/signer/publickey.cc new file mode 100644 index 0000000000..6dd99dd146 --- /dev/null +++ b/util/signer/publickey.cc @@ -0,0 +1,244 @@ +// +// Copyright 2015 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. +// + +#include "publickey.h" + +#include <string> + +#include <openssl/bn.h> +#include <openssl/evp.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/rand.h> + +PublicKey::PublicKey(const char* filename) { + EVP_PKEY* pkey = NULL; + BIO* bio = BIO_new(BIO_s_file()); + + if (BIO_read_filename(bio, filename) == 1) { + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + } + + if (NULL == pkey) { + fprintf(stderr, "loadKey: failed to load RSA key from '%s'", + filename); + } + + BIO_free_all(bio); + key = pkey; +} + +PublicKey::~PublicKey() { + if (key) { + EVP_PKEY_free(key); + key = NULL; + } +} + +bool PublicKey::ok() { + return key != NULL; +} + +size_t PublicKey::nwords() { + RSA* rsa = EVP_PKEY_get1_RSA(key); + size_t result = (BN_num_bytes(rsa->n) + 3) / 4; + RSA_free(rsa); + return result; +} + +uint32_t PublicKey::public_exponent() { + RSA* rsa = EVP_PKEY_get1_RSA(key); + uint32_t result = BN_get_word(rsa->e); + RSA_free(rsa); + return result; +} + +uint32_t PublicKey::n0inv() { + RSA* rsa = EVP_PKEY_get1_RSA(key); + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* r = BN_new(); + BIGNUM* rem = BN_new(); + BIGNUM* n0inv = BN_new(); + BN_set_bit(r, 32); // 2**32 + BN_div(NULL, rem, rsa->n, r, ctx); // low 32 bit + BN_mod_inverse(n0inv, rem, r, ctx); + + uint32_t result = 0 - BN_get_word(n0inv); + + BN_free(n0inv); + BN_free(rem); + BN_free(r); + BN_CTX_free(ctx); + RSA_free(rsa); + + return result; +} + +/*static*/ +void PublicKey::print(const char* tag, size_t nwords, BIGNUM* n) { + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* N = BN_new(); + BIGNUM* r = BN_new(); + BIGNUM* d = BN_new(); + BIGNUM* rem = BN_new(); + + BN_set_bit(r, 32); // 2^32 + BN_copy(N, n); + + printf("const uint32_t %s[%lu] = {", tag, nwords); + for (size_t i = 0; i < nwords; ++i) { + if (i) printf(", "); + BN_div(N, rem, N, r, ctx); + printf("0x%08lx", BN_get_word(rem)); + } + printf("};\n"); + + BN_free(rem); + BN_free(d); + BN_free(r); + BN_free(N); + BN_CTX_free(ctx); +} + +/*static*/ +void PublicKey::print(const char* tag, size_t nwords, + uint8_t* data, size_t len) { + BIGNUM* n = BN_bin2bn(data, len, NULL); + print(tag, nwords, n); + BN_free(n); +} + +void PublicKey::print(const char* tag) { + RSA* rsa = EVP_PKEY_get1_RSA(key); + print(tag, nwords(), rsa->n); + RSA_free(rsa); +} + +void PublicKey::printAll(const char* tag) { + std::string t(tag); + printf("#define %s_EXP %u\n", tag, public_exponent()); + printf("#define %s_INV 0x%08x\n", tag, n0inv()); + RSA* rsa = EVP_PKEY_get1_RSA(key); + print((t + "_MOD").c_str(), nwords(), rsa->n); + + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* RR = BN_new(); + BIGNUM* rem = BN_new(); + BIGNUM* quot = BN_new(); + BN_set_bit(RR, nwords() * 32 * 2); + BN_div(quot, rem, RR, rsa->n, ctx); + + print((t + "_RR").c_str(), nwords(), rem); + + BN_free(quot); + BN_free(rem); + BN_free(RR); + BN_CTX_free(ctx); + + RSA_free(rsa); +} + +/*static*/ +void PublicKey::toArray(uint32_t* dst, size_t nwords, BIGNUM* n) { + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* N = BN_new(); + BIGNUM* r = BN_new(); + BIGNUM* d = BN_new(); + BIGNUM* rem = BN_new(); + + BN_set_bit(r, 32); // 2^32 + BN_copy(N, n); + + for (size_t i = 0; i < nwords; ++i) { + BN_div(N, rem, N, r, ctx); + *dst++ = BN_get_word(rem); + } + + BN_free(rem); + BN_free(d); + BN_free(r); + BN_free(N); + BN_CTX_free(ctx); +} + +int PublicKey::encrypt(uint8_t* msg, int msglen, uint8_t* out) { + RSA* rsa = EVP_PKEY_get1_RSA(key); + int result = + RSA_public_encrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING); + RSA_free(rsa); + return result; +} + +int PublicKey::decrypt(uint8_t* msg, int msglen, uint8_t* out) { + RSA* rsa = EVP_PKEY_get1_RSA(key); + int result = + RSA_private_decrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING); + RSA_free(rsa); + return result; +} + + +int PublicKey::raw(uint8_t* in, int inlen, BIGNUM** out) { + RSA* rsa = EVP_PKEY_get1_RSA(key); + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* m = BN_new(); + BIGNUM* r = BN_new(); + BN_bin2bn(in, inlen, m); + int result = BN_mod_exp(r, m, rsa->d, rsa->n, ctx); + if (result == 1) { + *out = BN_dup(r); + } + BN_free(r); + BN_free(m); + BN_CTX_free(ctx); + RSA_free(rsa); + return result; +} + +// Sign message. +// Produces signature * R mod N (Montgomery format). +// Returns 1 on success. +int PublicKey::sign(const void* msg, size_t msglen, BIGNUM** output) { + int result = 0; + EVP_MD_CTX* ctx = NULL; + BN_CTX* bnctx = NULL; + BIGNUM* tmp = NULL; + RSA* rsa = NULL; + uint8_t* sig = NULL; + unsigned int siglen = 0; + + unsigned int tmplen = EVP_PKEY_size(key); + + ctx = EVP_MD_CTX_create(); + if (!ctx) goto __fail; + + EVP_MD_CTX_init(ctx); + EVP_DigestInit(ctx, EVP_sha256()); + if (EVP_DigestUpdate(ctx, msg, msglen) != 1) goto __fail; + + sig = (uint8_t*)malloc(tmplen); + result = EVP_SignFinal(ctx, sig, &siglen, key); + if (result != 1) goto __fail; + + tmp = BN_bin2bn(sig, siglen, NULL); + + // compute R*sig mod N + rsa = EVP_PKEY_get1_RSA(key); + if (BN_lshift(tmp, tmp, nwords() * 32) != 1) goto __fail; + + bnctx = BN_CTX_new(); + if (BN_mod(tmp, tmp, rsa->n, bnctx) != 1) goto __fail; + *output = BN_dup(tmp); + +__fail: + if (tmp) BN_free(tmp); + if (rsa) RSA_free(rsa); + if (sig) free(sig); + if (ctx) EVP_MD_CTX_destroy(ctx); + if (bnctx) BN_CTX_free(bnctx); + + return result; +} diff --git a/util/signer/publickey.h b/util/signer/publickey.h new file mode 100644 index 0000000000..0a33460433 --- /dev/null +++ b/util/signer/publickey.h @@ -0,0 +1,52 @@ +// +// Copyright 2015 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. +// + +#ifndef __EC_UTIL_SIGNER_PUBLICKEY_H +#define __EC_UTIL_SIGNER_PUBLICKEY_H + +#include <stddef.h> +#include <inttypes.h> + +typedef struct evp_pkey_st EVP_PKEY; +typedef struct bignum_st BIGNUM; + +class PublicKey { + public: + explicit PublicKey(const char* filename); + ~PublicKey(); + + bool ok(); + size_t nwords(); + uint32_t public_exponent(); + uint32_t n0inv(); + + // PKCS1.5 SHA256 + int sign(const void* msg, size_t msglen, BIGNUM** output); + + // PKCS1_OAEP SHA-1, MGF1 + int encrypt(uint8_t* in, int inlen, uint8_t* out); + + // PKCS1_OAEP SHA-1, MGF1 + int decrypt(uint8_t* in, int inlen, uint8_t* out); + + int raw(uint8_t* in, int inlen, BIGNUM** out); + + static void print(const char* tag, size_t nwords, uint8_t* data, size_t len); + static void print(const char* tag, size_t nwords, BIGNUM* n); + static void toArray(uint32_t* dst, size_t nwords, BIGNUM* n); + + void print(const char* tag); + void printAll(const char* tag); + void toArray(uint32_t* dst); + + private: + EVP_PKEY* key; + + PublicKey& operator=(const PublicKey& other); + PublicKey(const PublicKey& other); +}; + +#endif // __EC_UTIL_SIGNER_PUBLICKEY_H diff --git a/util/signer/rom-testkey.pem b/util/signer/rom-testkey.pem new file mode 100644 index 0000000000..9cd8662c64 --- /dev/null +++ b/util/signer/rom-testkey.pem @@ -0,0 +1,45 @@ +# +# Copyright 2015 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. +# +# openssl genrsa -3 3071 +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/util/signer/signed_header.h b/util/signer/signed_header.h new file mode 100644 index 0000000000..2207a08d11 --- /dev/null +++ b/util/signer/signed_header.h @@ -0,0 +1,29 @@ +// +// Copyright 2015 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. +// +#ifndef __EC_UTIL_SIGNER_SIGNED_HEADER_H +#define __EC_UTIL_SIGNER_SIGNED_HEADER_H + +#include <string.h> +#include <inttypes.h> + +typedef struct SignedHeader { + SignedHeader() : magic(-1), image_size(0) { + memset(signature, 'S', sizeof(signature)); + memset(tag, 'T', sizeof(tag)); + memset(fusemap, 0, sizeof(fusemap)); + memset(_pad, -1, sizeof(_pad)); + } + + uint32_t magic; // -1 + uint32_t image_size; // != -1 + uint32_t signature[96]; + uint32_t tag[8]; + uint32_t fusemap[32]; // 1024 bits + uint32_t _pad[256 - 1 - 1 - 96 - 8 - 32]; +} SignedHeader; +static_assert(sizeof(SignedHeader) == 1024, "SignedHeader should be 1024 bytes"); + +#endif // __EC_UTIL_SIGNER_SIGNED_HEADER_H |