summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2015-08-21 16:57:06 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-08-25 20:10:55 +0000
commitd9a614826b524c212b1ce449141a8af047178b38 (patch)
tree00e43f7828f374ad08f6aa6cd89619396e5abd49
parentb37514eed2873dcecc44619ea07542912aaf977b (diff)
downloadchrome-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>
-rw-r--r--util/signer/.gitignore1
-rw-r--r--util/signer/codesigner.cc122
-rw-r--r--util/signer/publickey.cc244
-rw-r--r--util/signer/publickey.h52
-rw-r--r--util/signer/rom-testkey.pem45
-rw-r--r--util/signer/signed_header.h29
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