summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.rules11
-rw-r--r--util/signer/aes.cc51
-rw-r--r--util/signer/build.mk12
-rw-r--r--util/signer/codesigner.cc454
-rw-r--r--util/signer/common/aes.h24
-rw-r--r--util/signer/common/ecdh.h24
-rw-r--r--util/signer/common/gnubby.h41
-rw-r--r--util/signer/common/image.h52
-rw-r--r--util/signer/common/publickey.h66
-rw-r--r--util/signer/common/signed_header.h48
-rw-r--r--util/signer/ecdh.cc47
-rw-r--r--util/signer/gnubby.cc491
-rw-r--r--util/signer/image.cc414
-rw-r--r--util/signer/publickey.cc115
-rw-r--r--util/signer/publickey.h52
-rw-r--r--util/signer/rom-testkey-A.pem364
-rw-r--r--util/signer/rom-testkey.pem45
-rw-r--r--util/signer/signed_header.h29
18 files changed, 2074 insertions, 266 deletions
diff --git a/Makefile.rules b/Makefile.rules
index e2951d095a..cdd75caa33 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -40,8 +40,9 @@ cmd_flat_to_obj = $(CC) -T $(out)/firmware_image.lds -nostdlib $(CPPFLAGS) \
# Allow the .roshared section to overlap other sections (itself)
cmd_ec_elf_to_flat ?= $(OBJCOPY) --set-section-flags .roshared=share \
-O binary $< $@
-cmd_raw_to_flat ?= $(out)/util/signer util/signer/rom-testkey.pem $< \
- && mv $<.signed $@
+cmd_elf_to_signed ?= $(out)/util/signer --key=util/signer/rom-testkey-A.pem \
+ --input=$< --format=bin --output=$@.signed \
+ && mv $@.signed $@
cmd_elf_to_dis = $(OBJDUMP) -D $< > $@
cmd_elf_to_hex = $(OBJCOPY) -O ihex $< $@
cmd_bin_to_hex = $(OBJCOPY) -I binary -O ihex \
@@ -57,7 +58,7 @@ cmd_c_to_build = $(BUILDCC) $(BUILD_CFLAGS) \
-MMD -MF $@.d -o $@
cmd_c_to_host = $(HOSTCC) $(HOST_CFLAGS) -MMD -MF $@.d -o $@ \
$(sort $(foreach c,$($(*F)-objs),util/$(c:%.o=%.c)) $*.c)
-cmd_cxx_to_host = $(HOSTCXX) -std=c++0x $(COMMON_WARN) \
+cmd_cxx_to_host = $(HOSTCXX) -std=c++0x $(COMMON_WARN) $(HOST_CXXFLAGS)\
-I ./$($(notdir $@)_ROOT) -o $@ $(filter %.cc,$^) $($(notdir $@)_LIBS)
cmd_host_test = ./util/run_host_test $* $(silent)
cmd_version = ./util/getversion.sh > $@
@@ -201,8 +202,8 @@ $(out)/RW/ec.RW.flat: $(out)/RW/ec.RW.elf
$(out)/RO/ec.RO.flat.raw: $(out)/RO/ec.RO.elf $(out)/RO/ec.RO.smap
$(call quiet,ec_elf_to_flat,OBJCOPY)
-$(out)/RO/ec.RO.flat: $(out)/RO/ec.RO.flat.raw
- $(call quiet,raw_to_flat,RO_SIGN)
+$(out)/RO/ec.RO.flat: $(out)/RO/ec.RO.elf $(out)/RO/ec.RO.smap
+ $(call quiet,elf_to_signed,RO_SIGN)
$(out)/RO/%.hex: $(out)/RO/%.flat
$(call quiet,bin_to_hex,OBJCOPY)
diff --git a/util/signer/aes.cc b/util/signer/aes.cc
new file mode 100644
index 0000000000..4d120a102a
--- /dev/null
+++ b/util/signer/aes.cc
@@ -0,0 +1,51 @@
+/* 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 <common/aes.h>
+
+#include <string.h>
+
+#include <openssl/aes.h>
+#include <openssl/cmac.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+AES::AES() {}
+AES::~AES() {
+ memset(key_, 0, sizeof(key_));
+}
+
+void AES::set_key(const void* key) {
+ memcpy(key_, key, sizeof(key_));
+}
+
+void AES::decrypt_block(const void* in, void* out) {
+ AES_KEY aes;
+ AES_set_decrypt_key(key_, sizeof(key_) * 8, &aes);
+ AES_decrypt(reinterpret_cast<const unsigned char*>(in),
+ reinterpret_cast<unsigned char*>(out), &aes);
+}
+
+void AES::encrypt_block(const void* in, void* out) {
+ AES_KEY aes;
+ AES_set_encrypt_key(key_, sizeof(key_) * 8, &aes);
+ AES_encrypt(reinterpret_cast<const unsigned char*>(in),
+ reinterpret_cast<unsigned char*>(out), &aes);
+}
+
+void AES::cmac(const void* in, size_t in_len, void* out) {
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+
+ SHA256_CTX sha;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha, reinterpret_cast<const unsigned char*>(in), in_len);
+ SHA256_Final(digest, &sha);
+
+ CMAC_CTX* cmac = CMAC_CTX_new();
+ CMAC_Init(cmac, key_, sizeof(key_), EVP_aes_128_cbc(), 0);
+ CMAC_Update(cmac, digest, sizeof(digest));
+ size_t out_len;
+ CMAC_Final(cmac, reinterpret_cast<unsigned char*>(out), &out_len);
+ CMAC_CTX_free(cmac);
+}
diff --git a/util/signer/build.mk b/util/signer/build.mk
index 7e7db38997..07db0ea7ea 100644
--- a/util/signer/build.mk
+++ b/util/signer/build.mk
@@ -2,15 +2,15 @@
# 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.
-#
-# Lock library
-#
-signer_LIBS := -lcrypto
+signer_LIBS := -lcrypto -lelf -lusb-1.0 -lxml2
signer_ROOT := util/signer
-SIGNER_DEPS := $(addprefix $(signer_ROOT)/, codesigner.cc \
- publickey.cc publickey.h signed_header.h)
+signer_INC := $(addprefix common/, aes.h ecdh.h gnubby.h \
+ image.h publickey.h signed_header.h)
+signer_SRC := codesigner.cc publickey.cc image.cc gnubby.cc aes.cc ecdh.cc
+SIGNER_DEPS := $(addprefix $(signer_ROOT)/, $(signer_SRC) $(signer_INC))
+HOST_CXXFLAGS += -I/usr/include/libxml2
$(out)/util/signer: $(SIGNER_DEPS)
$(call quiet,cxx_to_host,HOSTCXX)
diff --git a/util/signer/codesigner.cc b/util/signer/codesigner.cc
index 989e87b3c5..cf3513d773 100644
--- a/util/signer/codesigner.cc
+++ b/util/signer/codesigner.cc
@@ -1,122 +1,430 @@
-//
-// 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.
-//
+/* 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 <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <getopt.h>
+
+#include <common/image.h>
+#include <common/publickey.h>
+#include <common/signed_header.h>
+//#include <rapidjson/document.h>
+
+#include <string>
+#include <map>
+#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
-#include <ios>
-#include <string>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
-#include <openssl/pem.h>
+using namespace std;
+int last_logical_offset = -1;
+int fuse_index = 0;
+// Brute xml parsing.
+// Find HashItem w/ key == name, return val field, recursively.
+static
+xmlChar* get_val(xmlNodePtr node, const char* key) {
+ xmlNode* cur_node = NULL;
+ xmlChar* val = NULL;
-#include <signed_header.h>
-#include <publickey.h>
+ for (cur_node = node->children; cur_node; cur_node = cur_node->next) {
+ if (!strcmp("HashItem", (const char*)(cur_node->name))) {
+ // Hardcode parse <HashItem><Key>key</Key><Val>val</Val></HashItem>
+ xmlNodePtr key_node = cur_node->children->next;
+ xmlNodePtr val_node = cur_node->children->next->next->next;
+ xmlChar* keyName = xmlNodeGetContent(key_node);
+ xmlChar* valData = xmlNodeGetContent(val_node);
-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;
+ if (!strcmp(key, (const char*)keyName)) {
+ // Found our key, save val and done.
+ xmlFree(keyName);
+ val = valData;
+ break;
+ }
+ xmlFree(valData);
+ xmlFree(keyName);
+ }
-int LoadFlatFile(const char *name, size_t header_size)
-{
- ifstream::pos_type fileSize;
- ifstream FlatFile (name, ios::in | ios::binary | ios::ate);
+ val = get_val(cur_node, key);
+ if (val) {
+ // Found our key somewhere deeper down; done.
+ break;
+ }
+ }
- if(!FlatFile.is_open()) {
- fprintf(stderr, "failed to open %s\n", name);
- return -1;
+ return val;
+}
+
+static
+bool print_fuse(xmlNodePtr a_node,
+ map<string, uint32_t>* ids, map<string, uint32_t>* bits) {
+ bool result = false;
+
+ // Interested in <HashType>
+ if (strcmp("HashType", (const char*)(a_node->name))) {
+ return result;
}
- flat_size = FlatFile.tellg();
+ // Values we are interested in.
+ xmlChar* RegName = get_val(a_node, "RegName");
+ xmlChar* Width = get_val(a_node, "Width");
+ xmlChar* FuseLogicalOffset = get_val(a_node, "FuseLogicalOffset");
- 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;
+ // Track 1024 fuses at most.
+ int fuseLogicalOffset = atoi((const char*)FuseLogicalOffset);
+ if (fuseLogicalOffset >= last_logical_offset) {
+ last_logical_offset = fuseLogicalOffset;
+ ids->insert(make_pair((const char*)RegName, fuse_index++));
+ bits->insert(make_pair((const char*)RegName, atoi((const char*)Width)));
+ } else {
+ // Logical offset is regressing; assume we saw all the fuses.
+ // There are multiple sections that list all the fuses in the xml;
+ // we only care about parsing them once.
+ result = true;
}
- 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;
+ xmlFree(FuseLogicalOffset);
+ xmlFree(Width);
+ xmlFree(RegName);
+
+ return result;
+}
+
+static
+bool find_fuses(xmlNodePtr a_node,
+ map<string, uint32_t>* ids, map<string, uint32_t>* bits) {
+ xmlNode* cur_node = NULL;
+ bool done = false;
+
+ for (cur_node = a_node; !done && cur_node; cur_node = cur_node->next) {
+ xmlChar* content = NULL;
+
+ if (cur_node->type == XML_TEXT_NODE &&
+ (content = xmlNodeGetContent(cur_node)) != NULL) {
+ if (!strcmp("FuseLogicalOffset", (const char*)content)) {
+ // Found a likely fuse definition section; collect it.
+ done = print_fuse(a_node->parent->parent->parent, ids, bits);
+ }
}
- return 0;
+ if (content) xmlFree(content);
+
+ if (!done && cur_node->children) {
+ done = find_fuses(cur_node->children, ids, bits);
+ }
+ }
+
+ return done;
}
-int SaveSignedFile(const char *name)
-{
- FILE* fp = fopen(name, "wb");
+static
+bool find_default_reg_value(xmlNodePtr a_node,
+ const string& regname, string* result) {
+ xmlNode* cur_node = NULL;
+ bool done = false;
+
+ for (cur_node = a_node; !done && cur_node; cur_node = cur_node->next) {
+ xmlChar* content = NULL;
- if (!fp) {
- fprintf(stderr, "failed to open file '%s': %s\n", name, strerror(errno));
- return -1;
+ if (cur_node->type == XML_TEXT_NODE &&
+ (content = xmlNodeGetContent(cur_node)) != NULL) {
+ if (!strcmp(regname.c_str(), (const char*)content)) {
+ xmlChar* val = get_val(cur_node->parent->parent->parent, "Default");
+ if (val) {
+ result->assign((const char*)val);
+ xmlFree(val);
+ done = true;
+ }
+ }
+ }
+
+ if (content) xmlFree(content);
+
+ if (!done && cur_node->children) {
+ done = find_default_reg_value(cur_node->children, regname, result);
+ }
}
- 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;
+
+ return done;
+}
+
+
+// Read XML, populate two maps, name -> val
+bool readXML(const string& filename,
+ map<string, uint32_t>* ids,
+ map<string, uint32_t>* bits,
+ uint32_t* p4cl) {
+ bool result = false;
+ LIBXML_TEST_VERSION
+
+ xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0);
+
+ if (doc) {
+ result = find_fuses(xmlDocGetRootElement(doc), ids, bits);
+ string p4clStr;
+ result &= find_default_reg_value(xmlDocGetRootElement(doc), "SWDP_P4_LAST_SYNC", &p4clStr);
+ if (result) {
+ *p4cl = atoi(p4clStr.c_str());
+ }
+ xmlFreeDoc(doc);
}
- fclose(fp);
- return 0;
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ return result;
}
-// 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));
+// Read JSON, populate map, name -> val
+bool readJSON(const string& filename,
+ string* tag,
+ uint32_t* keyId,
+ uint32_t* p4cl,
+ map<string, uint32_t>* fusemap) {
+ bool result = false;
+#if 0
+ ifstream ifs(filename.c_str());
+ if (ifs) {
+
+ // Touch up a bit to allow for comments.
+ string s;
+ while (ifs) {
+ string line;
+ getline(ifs, line);
+ size_t nonspace = line.find_first_not_of(" \t");
+ if (nonspace != string::npos &&
+ line.find("//", nonspace) == nonspace) {
+ continue;
+ }
+ s.append(line);
+ }
- result = key.sign(&hdr->tag, flat_size - offsetof(SignedHeader, tag), &sig);
+ // Try parse.
+ rapidjson::Document d;
+ if (d.Parse(s.c_str()).HasParseError()) {
+ fprintf(stderr, "JSON %s[%lu]: parse error\n",
+ filename.c_str(), d.GetErrorOffset());
+ } else {
+ const rapidjson::Document::ValueType& fuses = d["fuses"];
+ for (auto it = fuses.MemberBegin(); it != fuses.MemberEnd(); ++it) {
+ fusemap->insert(make_pair(it->name.GetString(), it->value.GetInt()));
+ }
- 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);
+ const rapidjson::Document::ValueType& keyid = d["keyId"];
+ *keyId = keyid.GetInt();
+
+ const rapidjson::Document::ValueType& P4cl = d["p4cl"];
+ *p4cl = P4cl.GetInt();
+
+ const rapidjson::Document::ValueType& Tag = d["tag"];
+ tag->assign(Tag.GetString());
+
+ result = true;
+ }
}
+#endif
+ return result;
+}
- if (sig)
- BN_free(sig);
+string inputFilename;
+string outputFilename;
+string keyFilename;
+string xmlFilename;
+string jsonFilename;
+string outputFormat;
- return result != 1;
+void usage(int argc, char* argv[]) {
+ fprintf(stderr, "Usage: %s options\n"
+ "--input=$elf-filename\n"
+ "--output=output-filename\n"
+ "--key=$pem-filename\n"
+ "[--xml=$xml-filename] typically 'havenTop.xml'\n"
+ "[--json=$json-filename] the signing manifest\n"
+ "[--format=bin|hex] output file format, hex is default\n",
+ argv[0]);
+}
+
+int getOptions(int argc, char* argv[]) {
+ static struct option long_options[] = {
+ // name, has_arg
+ {"format", required_argument, NULL, 'f'},
+ {"help", no_argument, NULL, 'h'},
+ {"input", required_argument, NULL, 'i'},
+ {"json", required_argument, NULL, 'j'},
+ {"key", required_argument, NULL, 'k'},
+ {"output", required_argument, NULL, 'o'},
+ {"xml", required_argument, NULL, 'x'},
+ {0, 0, 0, 0}
+ };
+ int c, option_index = 0;
+ outputFormat.assign("hex");
+ while ((c = getopt_long(argc, argv, "i:o:k:x:j:f:h",
+ long_options, &option_index)) != -1) {
+ switch (c) {
+ case 0:
+ fprintf(stderr, "option %s", long_options[option_index].name);
+ if (optarg) fprintf(stderr, " with arg %s", optarg);
+ fprintf(stderr, "\n");
+ break;
+ case 'i':
+ inputFilename.assign(optarg);
+ break;
+ case 'o':
+ outputFilename.assign(optarg);
+ break;
+ case 'k':
+ keyFilename.assign(optarg);
+ break;
+ case 'x':
+ xmlFilename.assign(optarg);
+ break;
+ case 'j':
+ jsonFilename.assign(optarg);
+ break;
+ case 'f':
+ outputFormat.assign(optarg);
+ break;
+ case 'h':
+ usage(argc, argv);
+ return 1;
+ case '?':
+ // getopt_long printed error
+ return 1;
+ }
+ }
+ if (inputFilename.empty() ||
+ outputFilename.empty() ||
+ keyFilename.empty() ||
+ ((outputFormat != "bin") && (outputFormat != "hex"))) {
+ usage(argc, argv);
+ return 1;
+ }
+ return 0;
}
int main(int argc, char* argv[]) {
- if (argc < 3) {
- fprintf(stderr, "Usage: %s pem-file [hexfile|flatfile]\n", argv[0]);
+ if (getOptions(argc, argv)) {
exit(1);
}
- const char* arg = argv[2];
- PublicKey key(argv[1]);
+ PublicKey key(keyFilename);
if (!key.ok()) return -1;
+ // Load elf.
+ Image image;
+ if (!image.fromElf(inputFilename)) return -2;
+
SignedHeader hdr;
- // Load input file
- if (LoadFlatFile(arg, sizeof(hdr)))
- return -2;
+ hdr.keyid = key.n0inv();
+ hdr.ro_base = image.ro_base();
+ hdr.ro_max = image.ro_max();
+ hdr.rx_base = image.rx_base();
+ hdr.rx_max = image.rx_max();
+
+ // Parse signing manifest.
+ map<string, uint32_t> fuses;
+ uint32_t keyId = key.n0inv(); // default, in case no JSON.
+ uint32_t json_p4cl = 0;
+ string tag;
+
+ if (!jsonFilename.empty() &&
+ !readJSON(jsonFilename, &tag, &keyId, &json_p4cl, &fuses)) {
+ fprintf(stderr, "Failed to read JSON from '%s'\n", jsonFilename.c_str());
+ abort();
+ }
+
+ // Check keyId.
+ fprintf(stderr, "keyId: %08x\n", keyId);
+ if (keyId != hdr.keyid) {
+ fprintf(stderr, "mismatched keyid\n");
+ abort();
+ }
+
+ // Fill in tag.
+ fprintf(stderr, "tag: \"%s\"\n", tag.c_str());
+ strncpy((char*)(&hdr.tag), tag.c_str(), sizeof(hdr.tag));
+
+ // List the specific fuses and values.
+ fprintf(stderr, "care about %lu fuses:\n", fuses.size());
+ for (auto it : fuses) {
+ fprintf(stderr, "fuse '%s' should have value %u\n", it.first.c_str(), it.second);
+ }
+
+ // Parse xml.
+ map<string, uint32_t> fuse_ids;
+ map<string, uint32_t> fuse_bits;
+ uint32_t xml_p4cl = 0;
+
+ if (!xmlFilename.empty() &&
+ !readXML(xmlFilename, &fuse_ids, &fuse_bits, &xml_p4cl)) {
+ fprintf(stderr, "Failed to read XML from '%s'\n", xmlFilename.c_str());
+ abort();
+ }
+
+ if (json_p4cl != xml_p4cl) {
+ fprintf(stderr, "mismatching p4cl: xml %u vs. json %u\n",
+ xml_p4cl, json_p4cl);
+ abort();
+ }
+
+ fprintf(stderr, "found %lu fuse definitions\n", fuse_ids.size());
+ assert(fuse_ids.size() < FUSE_MAX);
+ for (auto it : fuse_ids) {
+ fprintf(stderr, "fuse '%s' at %u, width %u\n",
+ it.first.c_str(), it.second, fuse_bits[it.first]);
+ }
- if (sign(key, &hdr))
- return -3;
+ // Compute fuse_values array, according to manifest and xml.
+ uint32_t fuse_values[FUSE_MAX];
+ memset(fuse_values, FUSE_IGNORE, sizeof(fuse_values));
- if (SaveSignedFile((std::string(arg) + std::string(".signed")).c_str()))
- return -4;
+ for (auto x : fuses) {
+ map<string, uint32_t>::const_iterator it = fuse_ids.find(x.first);
+ if (it == fuse_ids.end()) {
+ fprintf(stderr, "cannot find definition for fuse '%s'\n", x.first.c_str());
+ abort();
+ }
+ uint32_t idx = it->second;
+ assert(idx < FUSE_MAX);
+ uint32_t mask = (1 << fuse_bits[x.first]) - 1;
+ if ((x.second & mask) != x.second) {
+ fprintf(stderr, "specified fuse value too large\n");
+ abort();
+ }
+ uint32_t val = FUSE_PADDING & ~mask;
+ val |= x.second;
+
+ fuse_values[idx] = val;
+ hdr.markFuse(idx);
+ }
+
+ // Print out fuse hash input.
+ fprintf(stderr, "expected fuse state:\n");
+ for (size_t i = 0; i < FUSE_MAX; ++i) {
+ fprintf(stderr, "%08x ", fuse_values[i]);
+ }
+ fprintf(stderr, "\n");
+
+ // Sign image.
+ if (image.sign(key, &hdr, fuse_values)) {
+ image.generate(outputFilename, outputFormat == "hex");
+ } else {
+ fprintf(stderr, "failed to sign\n");
+ }
return 0;
}
diff --git a/util/signer/common/aes.h b/util/signer/common/aes.h
new file mode 100644
index 0000000000..08e4eefb50
--- /dev/null
+++ b/util/signer/common/aes.h
@@ -0,0 +1,24 @@
+/* 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_COMMON_AES_H
+#define __EC_UTIL_SIGNER_COMMON_AES_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+class AES {
+ private:
+ unsigned char key_[16];
+ public:
+ AES();
+ ~AES();
+
+ void set_key(const void* key);
+ void decrypt_block(const void* in, void* out);
+ void encrypt_block(const void* in, void* out);
+ void cmac(const void* in, size_t in_len, void* out);
+};
+
+#endif // __EC_UTIL_SIGNER_COMMON_AES_H
diff --git a/util/signer/common/ecdh.h b/util/signer/common/ecdh.h
new file mode 100644
index 0000000000..52751491d7
--- /dev/null
+++ b/util/signer/common/ecdh.h
@@ -0,0 +1,24 @@
+/* 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_COMMON_ECDH_H
+#define __EC_UTIL_SIGNER_COMMON_ECDH_H
+
+#include <openssl/ec.h>
+
+class ECDH {
+ private:
+ EC_KEY* key_;
+ EC_GROUP* group_;
+ public:
+ ECDH();
+ ~ECDH();
+
+ void get_point(void* dst);
+
+ // Computes SHA256 of x-coordinate.
+ void compute_secret(const void* other, void* secret);
+};
+
+#endif // __EC_UTIL_SIGNER_COMMON_ECDH_H
diff --git a/util/signer/common/gnubby.h b/util/signer/common/gnubby.h
new file mode 100644
index 0000000000..8829862268
--- /dev/null
+++ b/util/signer/common/gnubby.h
@@ -0,0 +1,41 @@
+/* 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_COMMON_GNUBBY_H
+#define __EC_UTIL_SIGNER_COMMON_GNUBBY_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <libusb-1.0/libusb.h>
+
+typedef struct env_md_ctx_st EVP_MD_CTX;
+typedef struct evp_pkey_st EVP_PKEY;
+
+class Gnubby {
+ public:
+ Gnubby();
+ ~Gnubby();
+
+ bool ok() const { return handle_ != NULL; }
+
+ int Sign(EVP_MD_CTX* ctx, uint8_t* signature, uint32_t* siglen, EVP_PKEY* key);
+
+ private:
+ int send_to_device(uint8_t instruction,
+ const uint8_t* payload,
+ size_t length);
+
+ int receive_from_device(uint8_t* dest, size_t length);
+
+ libusb_context* ctx_;
+ libusb_device_handle* handle_;
+};
+
+#define MAX_APDU_SIZE 1200
+#define LIBUSB_ERR -1
+
+#define VERBOSE
+
+#endif // __EC_UTIL_SIGNER_COMMON_GNUBBY_H
diff --git a/util/signer/common/image.h b/util/signer/common/image.h
new file mode 100644
index 0000000000..c1468bb079
--- /dev/null
+++ b/util/signer/common/image.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_COMMON_IMAGE_H
+#define __EC_UTIL_SIGNER_COMMON_IMAGE_H
+
+#include <stdio.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <string>
+
+class PublicKey;
+struct SignedHeader;
+
+class Image {
+ public:
+ Image();
+ ~Image();
+
+ bool fromIntelHex(const std::string& filename, bool withSignature = true);
+ bool fromElf(const std::string& filename);
+
+ bool sign(PublicKey& key, const SignedHeader* hdr, const uint32_t fuses[]);
+ void generate(const std::string& outputFilename, bool hex_output) const;
+
+
+ bool ok() const { return success_; }
+ const uint8_t* code() const { return mem_; }
+ size_t size() const { return high_ - base_; }
+ int base() const { return base_; }
+ int ro_base() const { return ro_base_; }
+ int rx_base() const { return rx_base_; }
+ int ro_max() const { return ro_max_; }
+ int rx_max() const { return rx_max_; }
+
+ private:
+ void toIntelHex(FILE *fout) const;
+ int nibble(char n);
+ int parseByte(char** p);
+ int parseWord(char** p);
+ void store(int adr, int v);
+
+ bool success_;
+ uint8_t mem_[8*64*1024];
+ int low_, high_, base_;
+ size_t ro_base_, rx_base_;
+ size_t ro_max_, rx_max_;
+};
+
+#endif // __EC_UTIL_SIGNER_COMMON_IMAGE_H
diff --git a/util/signer/common/publickey.h b/util/signer/common/publickey.h
new file mode 100644
index 0000000000..6a899be9ac
--- /dev/null
+++ b/util/signer/common/publickey.h
@@ -0,0 +1,66 @@
+/* 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_COMMON_PUBLICKEY_H
+#define __EC_UTIL_SIGNER_COMMON_PUBLICKEY_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <string>
+
+typedef struct evp_pkey_st EVP_PKEY;
+typedef struct bignum_st BIGNUM;
+
+class PublicKey {
+ public:
+ explicit PublicKey(const std::string& filename);
+ ~PublicKey();
+
+ bool ok();
+
+ // # of words for R.
+ // Currently set at 3104 bits (97*32).
+ size_t rwords() const { return 96 + 1; }
+
+ // # of significant words in modulus.
+ size_t nwords();
+
+ uint32_t public_exponent();
+
+ // -1 / least significant word of modulus.
+ uint32_t n0inv();
+
+ // PKCS1.5 SHA256
+ // Mongomery factor 2**(32*rwords()) multiplied in.
+ 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);
+
+ void print(const char* tag, size_t nwords, uint8_t* data, size_t len);
+ void print(const char* tag, size_t nwords, BIGNUM* n);
+
+ static void toArray(uint32_t* dst, size_t nwords, BIGNUM* n);
+
+ // outputs rwords() words.
+ void print(const char* tag);
+
+ // outputs rwords() words.
+ void toArray(uint32_t* dst);
+
+ private:
+ EVP_PKEY* key_;
+ bool publicOnly_;
+
+ PublicKey& operator=(const PublicKey& other);
+ PublicKey(const PublicKey& other);
+};
+
+#endif // __EC_UTIL_SIGNER_COMMON_PUBLICKEY_H
diff --git a/util/signer/common/signed_header.h b/util/signer/common/signed_header.h
new file mode 100644
index 0000000000..d04f1ac088
--- /dev/null
+++ b/util/signer/common/signed_header.h
@@ -0,0 +1,48 @@
+/* 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_COMMON_SIGNED_HEADER_H
+#define __EC_UTIL_SIGNER_COMMON_SIGNED_HEADER_H
+
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define FUSE_PADDING 0x55555555
+#define FUSE_IGNORE 0xaaaaaaaa
+#define FUSE_MAX 160
+
+typedef struct SignedHeader {
+#ifdef __cplusplus
+ SignedHeader() : magic(-1), image_size(0) {
+ memset(signature, 'S', sizeof(signature));
+ memset(tag, 'T', sizeof(tag));
+ memset(fusemap, 0, sizeof(fusemap));
+ memset(_pad, 0xdd, sizeof(_pad));
+ }
+
+ void markFuse(uint32_t n) {
+ assert(n < FUSE_MAX);
+ fusemap[n / 32] |= 1 << (n & 31);
+ }
+#endif // __cplusplus
+
+ uint32_t magic; // -1
+ uint32_t signature[96 + 1];
+ uint32_t tag[7];
+ uint32_t keyid;
+ uint32_t image_size;
+ uint32_t ro_base;
+ uint32_t ro_max;
+ uint32_t rx_base;
+ uint32_t rx_max;
+ uint32_t fusemap[FUSE_MAX / (8 * sizeof(uint32_t))];
+ uint32_t _pad[256 - 1 - 97 - 7 - 6*1 - 5];
+} SignedHeader;
+
+#ifdef __cplusplus
+static_assert(sizeof(SignedHeader) == 1024, "SignedHeader should be 1024 bytes");
+#endif // __cplusplus
+
+#endif // __EC_UTIL_SIGNER_COMMON_SIGNED_HEADER_H
diff --git a/util/signer/ecdh.cc b/util/signer/ecdh.cc
new file mode 100644
index 0000000000..d5f9cfe25e
--- /dev/null
+++ b/util/signer/ecdh.cc
@@ -0,0 +1,47 @@
+/* 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 <common/ecdh.h>
+
+#include <openssl/obj_mac.h>
+#include <openssl/sha.h>
+
+ECDH::ECDH() {
+ key_ = EC_KEY_new();
+ group_ = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ EC_KEY_set_group(key_, group_);
+ EC_KEY_generate_key(key_);
+}
+
+ECDH::~ECDH() {
+ EC_GROUP_free(group_);
+ EC_KEY_free(key_);
+}
+
+void ECDH::get_point(void* dst) {
+ EC_POINT_point2oct(group_, EC_KEY_get0_public_key(key_),
+ POINT_CONVERSION_UNCOMPRESSED,
+ reinterpret_cast<unsigned char*>(dst), 65, 0);
+}
+
+void ECDH::compute_secret(const void* in, void* out) {
+ EC_POINT* b = EC_POINT_new(group_);
+ EC_POINT* x = EC_POINT_new(group_);
+
+ EC_POINT_oct2point(group_, b, reinterpret_cast<const unsigned char*>(in), 65, 0);
+ EC_POINT_mul(group_, x, 0, b, EC_KEY_get0_private_key(key_), 0);
+
+ unsigned char xbytes[65];
+ EC_POINT_point2oct(group_, x,
+ POINT_CONVERSION_UNCOMPRESSED,
+ xbytes, sizeof(xbytes), 0);
+
+ SHA256_CTX sha;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha, xbytes + 1, 32); // x coordinate only
+ SHA256_Final(reinterpret_cast<unsigned char*>(out), &sha);
+
+ EC_POINT_free(x);
+ EC_POINT_free(b);
+}
diff --git a/util/signer/gnubby.cc b/util/signer/gnubby.cc
new file mode 100644
index 0000000000..8e10181ea0
--- /dev/null
+++ b/util/signer/gnubby.cc
@@ -0,0 +1,491 @@
+/* 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 <common/gnubby.h>
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include <libusb-1.0/libusb.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <common/aes.h>
+#include <common/ecdh.h>
+
+#include <string>
+
+// Largely from gnubby ifd_driver.c
+// -----
+
+typedef int RESPONSECODE;
+typedef uint8_t UCHAR;
+typedef UCHAR* PUCHAR;
+typedef uint32_t DWORD;
+typedef DWORD* PDWORD;
+
+#define IFD_SUCCESS 0
+#define IFD_COMMUNICATION_ERROR -1
+
+//#define DLOG(...) fprintf(stderr, __VA_ARGS__)
+#define DLOG(...)
+
+// usb gnubby commands
+#define CMD_ATR 0x81
+#define CMD_APDU 0x83
+#define CMD_LOCK 0x84
+#define CMD_WINK 0x88
+
+// Helper to dump bits to console.
+static
+void printHex(const char* text, const void* data, int len) {
+ int i;
+ const uint8_t* d = (const uint8_t*)(data);
+ (void)d;
+ DLOG("%s: ", text);
+ for (i = 0; i < len; ++i) {
+ DLOG("%02x", *d++);
+ if (i == 3) DLOG(":");
+ if (i == 4) DLOG("|");
+ }
+ DLOG("\n");
+}
+
+// Construct usb framed request.
+// data can be NULL iff len == 0.
+// Returns frame size > 0 on success.
+static
+RESPONSECODE construct_usb_frame(uint8_t cmd,
+ const void* data, DWORD len,
+ uint8_t* out, PDWORD out_len) {
+ const uint8_t* d = (const uint8_t*)(data);
+ DWORD i;
+
+ if (*out_len < len + 7) return IFD_COMMUNICATION_ERROR;
+
+ // use pid as channel id
+ out[0] = getpid() >> 0;
+ out[1] = getpid() >> 8;
+ out[2] = getpid() >> 16;
+ out[3] = getpid() >> 24;
+ out[4] = cmd;
+ out[5] = len >> 8;
+ out[6] = len;
+
+ // Append the actual payload.
+ for (i = 0; i < len; ++i) out[7 + i] = d[i];
+
+ // Return total length
+ *out_len = 7 + len;
+
+ return IFD_SUCCESS;
+}
+
+// Send cmd to gnubby and receive response.
+static
+RESPONSECODE gnubby_exchange(libusb_device_handle* dev_handle,
+ void* buf, int res,
+ void* rsp, PDWORD rsp_len) {
+ int sent_len = 0;
+ uint8_t rcv[2048];
+ int recv_len = 0;
+
+ DLOG("gnubby_exchange(%p, %p, %d, %p, *%u)\n",
+ dev_handle, buf, res, rsp, *rsp_len);
+
+ printHex(">", buf, res);
+
+ // Send to gnubby
+ res = libusb_bulk_transfer(dev_handle, (1 | LIBUSB_ENDPOINT_OUT),
+ (unsigned char*)(buf), res,
+ &sent_len, 0);
+ DLOG(">: libusb_bulk_transfer: %d [%d]\n", res, sent_len);
+ if (res < 0) return IFD_COMMUNICATION_ERROR;
+
+ // Read from gnubby
+ memset(rcv, 0, sizeof(rcv)); // start clean.
+ res = libusb_bulk_transfer(dev_handle, (1 | LIBUSB_ENDPOINT_IN),
+ rcv, sizeof rcv, &recv_len, 0);
+ DLOG("<: libusb_bulk_transfer: %d [%d]\n", res, recv_len);
+ if (res < 0) return IFD_COMMUNICATION_ERROR;
+
+ if (recv_len > 0) {
+ printHex("<", rcv, recv_len);
+
+ // Check return header.
+ // rcv[0..4] should be equal to request.
+ // rcv[5..6] is response payload length.
+ // rcv[recv_len-2..recv_len-1] is smartcard response code (9000 etc.)
+ if (memcmp(buf, rcv, 5)) return IFD_COMMUNICATION_ERROR;
+
+ uint16_t plen = rcv[5] * 256 + rcv[6];
+ if (plen + 7 < recv_len) return IFD_COMMUNICATION_ERROR;
+
+ if (*rsp_len < plen) return IFD_COMMUNICATION_ERROR;
+
+ // Copy response payload.
+ memcpy(rsp, rcv + 7, plen);
+
+ // Return payload length.
+ *rsp_len = plen;
+
+ return IFD_SUCCESS;
+ }
+
+ return IFD_COMMUNICATION_ERROR;
+}
+
+#if 0
+static
+RESPONSECODE gnubby_atr(libusb_device_handle* handle, PUCHAR Atr, PDWORD AtrLength) {
+ uint8_t cmd[10];
+ DWORD cmd_len = sizeof(cmd);
+ RESPONSECODE res;
+
+ DLOG("gnubby_atr(%p, %p, *%u)\n", handle, Atr, *AtrLength);
+
+ memset(Atr, 0, *AtrLength);
+
+ res = construct_usb_frame(CMD_ATR, NULL, 0, cmd, &cmd_len);
+ if (res != IFD_SUCCESS) return res;
+
+ res = gnubby_exchange(handle, cmd, cmd_len, Atr, AtrLength);
+
+ if (res == IFD_SUCCESS) {
+ // Present an ATR that can do T=1
+ // Gnubby ATR appears to not advertise that capability.
+ memcpy(Atr, "\x3B\xF0\x13\x00\x00\x81\x31\xFE\x45\xE8", 10);
+ *AtrLength = 10;
+ }
+
+ return res;
+}
+#endif
+
+static
+RESPONSECODE gnubby_apdu(libusb_device_handle* handle,
+ PUCHAR tx, DWORD txLen,
+ PUCHAR rx, PDWORD rxLen) {
+ uint8_t cmd[2048];
+ DWORD cmd_len = sizeof(cmd);
+ RESPONSECODE res = IFD_SUCCESS;
+
+ DLOG("gnubby_apdu(%p, %p, %u, %p, *%u)\n",
+ handle, tx, txLen, rx, *rxLen);
+
+ res = construct_usb_frame(CMD_APDU, tx, txLen, cmd, &cmd_len);
+ if (res != IFD_SUCCESS) return res;
+
+ res = gnubby_exchange(handle, cmd, cmd_len, rx, rxLen);
+
+ if (res != IFD_SUCCESS) *rxLen = 0;
+ return res;
+}
+
+static
+RESPONSECODE gnubby_lock(libusb_device_handle* handle, UCHAR seconds) {
+ uint8_t cmd[10];
+ DWORD cmd_len = sizeof(cmd);
+ uint8_t rsp[10];
+ DWORD rsp_len = sizeof(rsp);
+
+ RESPONSECODE res = IFD_SUCCESS;
+
+ res = construct_usb_frame(CMD_LOCK, &seconds, 1, cmd, &cmd_len);
+ if (res != IFD_SUCCESS) return res;
+
+ res = gnubby_exchange(handle, cmd, cmd_len, rsp, &rsp_len);
+ if (res != IFD_SUCCESS) return res;
+
+ if ((rsp_len == 1 && rsp[0] == 0) ||
+ rsp_len == 0) {
+ return IFD_SUCCESS;
+ }
+
+ return IFD_COMMUNICATION_ERROR;
+}
+
+static
+RESPONSECODE gnubby_wink(libusb_device_handle* handle) {
+ uint8_t cmd[10];
+ DWORD cmd_len = sizeof(cmd);
+ uint8_t rsp[10];
+ DWORD rsp_len = sizeof(rsp);
+
+ RESPONSECODE res = IFD_SUCCESS;
+
+ res = construct_usb_frame(CMD_WINK, NULL, 0, cmd, &cmd_len);
+ if (res != IFD_SUCCESS) return res;
+
+ res = gnubby_exchange(handle, cmd, cmd_len, rsp, &rsp_len);
+ if (res != IFD_SUCCESS) return res;
+
+ return res;
+}
+
+// -----
+// end of ifd_driver cut&paste
+
+
+// Open a usb device and return (handle_, context).
+Gnubby::Gnubby() {
+ libusb_init(&ctx_);
+ libusb_set_debug(ctx_, 3);
+
+ handle_ = libusb_open_device_with_vid_pid(
+ ctx_,
+ 0x1050, // Gnubby Vendor ID (VID)
+ 0x0211 // Gnubby Product ID (PID)
+ );
+ DLOG("gnubby dev_handle_ %p\n", handle_);
+ int rc = libusb_claim_interface(handle_, 0);
+ DLOG("gnubby claim : %d\n", rc);
+
+ if (rc != 0) {
+ if (handle_) libusb_close(handle_);
+ handle_ = NULL;
+ }
+}
+
+// Close a usb device.
+Gnubby::~Gnubby() {
+ // Close handle_ if non-zero.
+ if (handle_) {
+ int rc = libusb_release_interface(handle_, 0);
+ DLOG("gnubby release : %d\n", rc);
+ libusb_close(handle_);
+ (void) rc;
+ }
+
+ // Close context.
+ libusb_exit(ctx_);
+}
+
+
+static
+int getSW12(const uint8_t* buf, size_t buflen) {
+ if (buflen < 2) return -1;
+ return buf[buflen - 2] * 256 + buf[buflen - 1];
+}
+
+static
+void getPIN(uint8_t* out) {
+ srand(time(NULL)); // yuk
+ for (int i = 0; i < 16; ++i) out[i] = (uint32_t)rand() >> (i+1);
+
+ const char* pin = getpass("Gnubby PIN: ");
+ int len = strlen(pin);
+
+ if (len == 6) {
+ // Exactly 6, copy direct.
+ memcpy(out, pin, 6);
+ } else {
+ // SHA256, take first 6.
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha, pin, len);
+ SHA256_Final(digest, &sha);
+ memcpy(out, digest, 6);
+ }
+}
+
+static
+std::string tokenFilename(const uint8_t* fp) {
+ const char* home = getenv("HOME");
+ if (home == NULL) getpwuid(getuid())->pw_dir;
+ std::string s(home);
+ s.append("/.tmp/");
+ for (int i = 0; i < 32; ++i) {
+ s.push_back("0123456789abcdef"[fp[i]>>4]);
+ s.push_back("0123456789abcdef"[fp[i]&15]);
+ }
+ s.append(".token");
+ return s;
+}
+
+static
+bool getToken(const uint8_t* fp, uint8_t* out) {
+ int fd = open(tokenFilename(fp).c_str(), O_RDONLY);
+ if (fd < 0) return false;
+ int n = read(fd, out, 16);
+ close(fd);
+ DLOG("read %d from %s\n", n, tokenFilename(fp).c_str());
+ return n == 16;
+}
+
+static
+void saveToken(const uint8_t* fp, const uint8_t* token) {
+ int fd = open(tokenFilename(fp).c_str(), O_CREAT|O_TRUNC|O_APPEND|O_NOFOLLOW|O_WRONLY, 0600);
+ if (fd >= 0) {
+ int n = write(fd, token, 16);
+ DLOG("wrote %d to %s\n", n, tokenFilename(fp).c_str());
+ close(fd);
+ (void) n;
+ }
+}
+
+static
+void forgetToken(const uint8_t* fp) {
+ DLOG("forgetting token %s\n", tokenFilename(fp).c_str());
+ unlink(tokenFilename(fp).c_str());
+}
+
+int Gnubby::Sign(EVP_MD_CTX* ctx, uint8_t* signature, uint32_t* siglen,
+ EVP_PKEY* key) {
+ // build pkcs1.5 request for ctx hash
+ // lock(100)
+ // select ssh
+ // read slot 0x66
+ // compare against key
+ // try read token from ~/.tmp/attest-fp
+ // loop
+ // - send sign request
+ // - handle PIN, touch
+ // unlock()
+ // profit!
+ RESPONSECODE result = -1;
+
+ uint8_t fp[32];
+ ECDH ecdh;
+ uint8_t secret[32] = {0};
+ AES aes;
+ uint8_t pin[16];
+ uint8_t token[16];
+
+ UCHAR req[1024];
+ UCHAR resp[2048];
+ DWORD resp_len = 0;
+
+ DWORD image_hash_len = SHA256_DIGEST_LENGTH;
+
+ // Compute pkc15 padded inputs for requested sha256.
+ // Brutal hard-coding ftw.
+ uint8_t padded_req[256];
+ memset(padded_req, 0xff, sizeof(padded_req));
+ padded_req[0] = 0x00;
+ padded_req[1] = 0x01;
+ // Fixed asn1 riddle for sha256
+ memcpy(padded_req + 256 - 32 - 20,
+ "\x00\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20",
+ 20);
+ // Append sha256
+ EVP_DigestFinal_ex(ctx,
+ padded_req + sizeof(padded_req) - SHA256_DIGEST_LENGTH,
+ &image_hash_len);
+
+ // lock(100)
+ result = gnubby_lock(handle_, (UCHAR)100);
+ if (result != 0) goto __fail;
+ // TODO: handle busy etc.
+
+ // select ssh applet
+ resp_len = sizeof(resp);
+ result = gnubby_apdu(handle_,
+ (PUCHAR)"\x00\xa4\x04\x00\x06\x53\x53\x48\x00\x01\x01", 11,
+ resp, &resp_len);
+ if (result != 0) goto __fail;
+ if (getSW12(resp, resp_len) != 0x9000) goto __fail;
+
+__again:
+
+ // read slot 0x66 under challenge
+ resp_len = sizeof(resp);
+ result = gnubby_apdu(handle_,
+ (PUCHAR)"\x00\x43\x66\x00\x00\x00\x10\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", 5 + 2 + 16,
+ resp, &resp_len);
+ if (result != 0) goto __fail;
+ if (getSW12(resp, resp_len) != 0x9000) goto __fail;
+
+ // save device fingerprint
+ memcpy(fp, resp + 1 + 256 + 65 + 65, 32);
+
+ // TODO: compare against key
+
+ if (!getToken(fp, token)) {
+ // PIN unlock required.
+ getPIN(pin);
+
+ ecdh.compute_secret(resp + 1 + 256, secret);
+ aes.set_key(secret);
+
+ memcpy(req, "\x00\x42\x00\x00\x51", 5);
+
+ ecdh.get_point(req + 5);
+ printHex("req", req, 5 + 65);
+ aes.encrypt_block(pin, req + 5 + 65);
+ printHex("req", req, 5 + 65 + 16);
+
+ resp_len = sizeof(resp);
+ result = gnubby_apdu(handle_,
+ req, 5 + 65 + 16,
+ resp, &resp_len);
+ if (result != 0) goto __fail;
+
+ if ((getSW12(resp, resp_len) & 0xfff0) == 0x63c0) {
+ // Wrong PIN.
+ goto __again;
+ }
+
+ if (getSW12(resp, resp_len) != 0x9000) goto __fail;
+
+ aes.set_key(secret + 16);
+ aes.decrypt_block(resp, token);
+
+ saveToken(fp, token);
+ }
+
+ // Build sign request using slot 0x66.
+ memset(req, 0, sizeof(req));
+ memcpy(req, "\x00\x40\x66\x00\x00\x01\x10", 7);
+ memcpy(req + 7 + 16, padded_req, 256);
+
+ aes.set_key(token);
+ aes.cmac(req + 7 + 16, 256, req + 7);
+
+ for (;;) {
+ resp_len = sizeof(resp);
+ result = gnubby_apdu(handle_,
+ req, 7 + 256 + 16,
+ resp, &resp_len);
+ if (result != 0) goto __fail;
+
+ if (getSW12(resp, resp_len) == 0x6985) { // touch
+ gnubby_wink(handle_);
+ fprintf(stderr, "touch..");
+ fflush(stderr);
+ usleep(200000); // slow down, buddy
+ continue;
+ }
+
+ if (getSW12(resp, resp_len) == 0x63ca) { // pin
+ forgetToken(fp);
+ goto __again;
+ }
+
+ break;
+ }
+
+ if (getSW12(resp, resp_len) != 0x9000) goto __fail;
+
+ // Return signature.
+ memcpy(signature, resp, 256);
+ *siglen = 256;
+
+ // profit!
+ result = 1;
+
+__fail:
+ // (always try to) unlock
+ gnubby_lock(handle_, 0);
+
+ return result;
+}
diff --git a/util/signer/image.cc b/util/signer/image.cc
new file mode 100644
index 0000000000..4b9db30608
--- /dev/null
+++ b/util/signer/image.cc
@@ -0,0 +1,414 @@
+/* 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 <common/image.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libelf.h>
+#include <gelf.h>
+
+#include <common/publickey.h>
+
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+#include <common/signed_header.h>
+
+#include <string>
+
+using namespace std;
+
+static const int FLASH_START = 0x4000;
+static const int FLASH_END = FLASH_START + 512 * 1024;
+
+Image::Image()
+ : success_(true), low_(FLASH_END - FLASH_START), high_(0),
+ base_(0), ro_base_(FLASH_END*16), rx_base_(FLASH_END*16),
+ ro_max_(0), rx_max_(0) {
+ memset(mem_, 0xff, sizeof(mem_)); // default memory content
+}
+
+bool Image::fromElf(const string& filename) {
+ Elf* elf = NULL;
+ Elf_Scn* scn = NULL;
+ GElf_Shdr shdr;
+ GElf_Phdr phdr;
+
+ char* base_ptr = NULL;
+ struct stat elf_stats;
+
+ bool result = false;
+
+ int fd;
+
+ if ((fd = open(filename.c_str(), O_RDONLY)) < 0) {
+ fprintf(stderr, "failed to open '%s'\n", filename.c_str());
+ goto fail;
+ }
+ if ((fstat(fd, &elf_stats)) < 0) {
+ fprintf(stderr, "cannot stat '%s'\n", filename.c_str());
+ goto fail;
+ }
+
+// printf("Elf filesize: %lu\n", elf_stats.st_size);
+
+ if ((base_ptr = (char*) malloc(elf_stats.st_size)) == NULL) {
+ fprintf(stderr, "cannot malloc %lu\n", elf_stats.st_size);
+ goto fail;
+ }
+
+ if (read(fd, base_ptr, elf_stats.st_size) < elf_stats.st_size) {
+ fprintf(stderr, "cannot read from '%s'\n", filename.c_str());
+ goto fail;
+ }
+
+ // Sniff content for sanity
+ if (*(uint32_t*) base_ptr != 0x464c457f) {
+// fprintf(stderr, "'%s' is not elf file\n", filename);
+ goto fail;
+ }
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ fprintf(stderr, "Warning: elf library is out of data!\n");
+ }
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+
+ // Infer minimal rx segment from section headers.
+ while ((scn = elf_nextscn(elf, scn)) != 0) {
+ gelf_getshdr(scn, &shdr);
+
+ printf("type %08x; flags %08lx ", shdr.sh_type, shdr.sh_flags);
+ printf("%08lx(@%08lx)[%08lx] align %lu\n",
+ shdr.sh_addr, shdr.sh_offset, shdr.sh_size, shdr.sh_addralign);
+
+ // Ignore sections that are not alloc
+ if (!(shdr.sh_flags & SHF_ALLOC)) {
+ continue;
+ }
+
+ // Ignore sections that are not exec
+ if (!(shdr.sh_flags & SHF_EXECINSTR)) {
+ continue;
+ }
+
+ // Ignore sections outside our flash range
+ if (shdr.sh_addr < FLASH_START * 16 ||
+ shdr.sh_addr + shdr.sh_size >= FLASH_END * 16) {
+ continue;
+ }
+
+ // Track rx boundaries
+ if (shdr.sh_addr < rx_base_) {
+ rx_base_ = shdr.sh_addr;
+ }
+ if (shdr.sh_addr + shdr.sh_size> rx_max_) {
+ rx_max_ = shdr.sh_addr + shdr.sh_size;
+ }
+ }
+
+ // Load image per program headers and track total ro segment
+ for (int index = 0; gelf_getphdr(elf, index, &phdr); ++index) {
+ printf("phdr %08lx(@%08lx) [%08lx/%08lx]",
+ phdr.p_vaddr, phdr.p_paddr, phdr.p_filesz, phdr.p_memsz);
+
+ if (phdr.p_filesz != phdr.p_memsz) {
+ printf(" (not loading)\n");
+ continue;
+ }
+
+ // Ignore sections outside our flash range
+ if (phdr.p_paddr < FLASH_START * 16 ||
+ phdr.p_paddr + phdr.p_memsz >= FLASH_END * 16) {
+ continue;
+ }
+
+ printf("\n");
+
+ // Track ro boundaries
+ if (phdr.p_paddr < ro_base_) {
+ ro_base_ = phdr.p_paddr;
+ }
+ if (phdr.p_paddr + phdr.p_memsz > ro_max_) {
+ ro_max_ = phdr.p_paddr + phdr.p_memsz;
+ }
+
+ // Copy data into image
+ for (size_t n = 0; n < phdr.p_filesz; ++n) {
+ store(phdr.p_paddr + n - FLASH_START * 16,
+ base_ptr[phdr.p_offset + n]);
+ }
+ }
+
+ low_ &= ~2047;
+ base_ = low_;
+
+ if (rx_base_ < base_ + FLASH_START * 16 + sizeof(SignedHeader)) {
+ // Fix-up 1K header that is part of rx in EC builds
+ rx_base_ = base_ + FLASH_START * 16 + sizeof(SignedHeader);
+ }
+
+ high_ = ((high_ + 2047) / 2048) * 2048; // Round image to multiple of 2K.
+
+ printf("Rounded image size %lu\n", size());
+ printf("ro_base %08lx..%08lx\n", ro_base_, ro_max_);
+ printf("rx_base %08lx..%08lx\n", rx_base_, rx_max_);
+
+ result = true;
+
+fail:
+ if (elf) elf_end(elf);
+ if (base_ptr) free(base_ptr);
+ if (fd >= 0) close(fd);
+
+ return result;
+}
+
+bool Image::fromIntelHex(const string& filename, bool withSignature) {
+ bool isRam = false;
+ int seg = 0;
+
+ FILE* fp = fopen(filename.c_str(), "rt");
+ if (fp != NULL) {
+ char line[BUFSIZ];
+ while (fgets(line, sizeof(line), fp)) {
+ if (strchr(line, '\r')) *strchr(line, '\r') = 0;
+ if (strchr(line, '\n')) *strchr(line, '\n') = 0;
+ if (line[0] != ':') continue; // assume comment line
+ if (strlen(line) < 9) {
+ fprintf(stderr, "short record %s", line);
+ success_ = false;
+ continue;
+ }
+ if (line[7] != '0') {
+ fprintf(stderr, "unknown record type %s", line);
+ success_ = false;
+ } else switch (line[8]) {
+ case '1': { // 01 eof
+ } break;
+ case '2': { // 02 segment
+ if (!strncmp(line, ":02000002", 9)) {
+ char* p = line + 9;
+ int s = parseWord(&p);
+ if (s != 0x1000) {
+ if (s >= FLASH_START && s <= FLASH_END) {
+ seg = s - FLASH_START;
+ //fprintf(stderr, "at segment %04x\n", seg);
+ } else {
+ fprintf(stderr, "data should in range %x-%x: %s\n",
+ FLASH_START, FLASH_END, line);
+ success_ = false;
+ }
+ }
+ }
+ isRam = !strcmp(line, ":020000021000EC");
+ } break;
+ case '0': { // 00 data
+ char* p = line + 1;
+ int len = parseByte(&p);
+ int adr = parseWord(&p);
+ parseByte(&p);
+ while (len--) {
+ if (isRam) {
+ int v = parseByte(&p);
+ if (v != 0) {
+ fprintf(stderr, "WARNING: non-zero RAM byte %02x at %04x\n",
+ v, adr);
+
+ }
+ ++adr;
+ } else {
+ store((seg * 16) + adr++, parseByte(&p));
+ }
+ }
+ } break;
+ case '3': { // 03 entry point
+ } break;
+ default: {
+ fprintf(stderr, "unknown record type %s", line);
+ success_ = false;
+ } break;
+ }
+ }
+ fclose(fp);
+ } else {
+ fprintf(stderr, "failed to open file '%s'\n", filename.c_str());
+ success_ = false;
+ }
+
+ if (success_) {
+ static_assert(sizeof(SignedHeader) == 1024,
+ "SignedHeader should be 1024 bytes");
+ if (withSignature) {
+ // signed images start on 2K boundary.
+ if ((low_ & 2047) != 0) {
+ fprintf(stderr, "signed images should start on 2K boundary, not %08x\n", low_);
+ }
+ base_ = low_;
+ } else {
+ // unsigned images start on odd 1K boundary.
+ if ((low_ & 2047) != 1024) {
+ fprintf(stderr, "unsigned images should start odd 1K boundary, not %08x\n", low_);
+ }
+ base_ = low_ - sizeof(SignedHeader);
+ }
+ }
+
+ if (success_) {
+ fprintf(stderr, "low %08x, high %08x\n",
+ FLASH_START * 16 + low_, FLASH_START * 16 + high_);
+ // Round image to multiple of 2K.
+ high_ = ((high_ + 2047) / 2048) * 2048;
+ ro_base_ = FLASH_START * 16 + base_;
+ rx_base_ = FLASH_START * 16 + base_;
+ ro_max_ = FLASH_START * 16 + base_ + size();
+ rx_max_ = FLASH_START * 16 + base_ + size();
+ fprintf(stderr, "base %08lx, size %08lx\n", ro_base_, size());
+ }
+
+ return success_;
+}
+
+Image::~Image() {}
+
+void Image::toIntelHex(FILE *fout) const {
+ for (int i = base_; i < high_; i += 16) {
+ // spit out segment record at start of segment.
+ if (!((i - base_)&0xffff)) {
+ int s = FLASH_START + (base_>>4) + ((i - base_)>>4);
+ fprintf(fout, ":02000002%04X%02X\n", s,
+ (~((2 + 2 + (s>>8)) & 255) + 1) & 255);
+ }
+ // spit out data records, 16 bytes each.
+ fprintf(fout, ":10%04X00", (i - base_)&0xffff);
+ int crc = 16 + (((i - base_)>>8)&255) + ((i - base_)&255);
+ for (int n = 0; n < 16; ++n) {
+ fprintf(fout, "%02X", mem_[i+n]);
+ crc += mem_[i+n];
+ }
+ fprintf(fout, "%02X", (~(crc & 255) + 1) & 255);
+ fprintf(fout, "\n");
+ }
+}
+
+void Image::generate(const std::string& filename, bool hex_output) const {
+ FILE* fout = fopen(filename.c_str(), "w");
+ if (!fout) return;
+
+ if (hex_output)
+ toIntelHex(fout);
+ else // TODO: we don't expect write to fail, can be made more robust.
+ fwrite(mem_, 1, high_ - base_, fout);
+ fclose(fout);
+}
+
+
+int Image::nibble(char n) {
+ switch (n) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return n - '0';
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ default:
+ fprintf(stderr, "bad hex digit '%c'\n", n);
+ success_ = false;
+ return 0;
+ }
+}
+
+int Image::parseByte(char** p) {
+ int result = nibble(**p);
+ result *= 16;
+ (*p)++;
+ result |= nibble(**p);
+ (*p)++;
+ return result;
+}
+
+int Image::parseWord(char** p) {
+ int result = parseByte(p);
+ result *= 256;
+ result |= parseByte(p);
+ return result;
+}
+
+void Image::store(int adr, int v) {
+ if (adr < 0 || (size_t)(adr) >= sizeof(mem_)) {
+ fprintf(stderr, "illegal adr %04x\n", adr);
+ success_ = false;
+ return;
+ }
+ mem_[adr] = v;
+ if (adr > high_) high_ = adr;
+ if (adr < low_) low_ = adr;
+}
+
+bool Image::sign(PublicKey& key, const SignedHeader* input_hdr,
+ const uint32_t fuses[FUSE_MAX]) {
+ BIGNUM* sig = NULL;
+ SignedHeader* hdr = (SignedHeader*)(&mem_[base_]);
+ SHA256_CTX sha256;
+ int result;
+
+ // List of hashes we actually sign.
+ struct {
+ uint8_t img_hash[SHA256_DIGEST_LENGTH];
+ uint8_t fuses_hash[SHA256_DIGEST_LENGTH];
+ // TODO: flash_fuses_hash
+ } hashes;
+
+ memcpy(hdr, input_hdr, sizeof(SignedHeader));
+ hdr->image_size = this->size();
+
+ // Hash img
+ int size = this->size() - offsetof(SignedHeader, tag);
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, &hdr->tag, size);
+ SHA256_Final(hashes.img_hash, &sha256);
+
+ fprintf(stderr, "img hash :");
+ for (size_t i = 0; i < sizeof(hashes.img_hash); ++i) {
+ fprintf(stderr, "%02x", hashes.img_hash[i]);
+ }
+ fprintf(stderr, "\n");
+
+ // Hash fuses
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, fuses, FUSE_MAX * sizeof(uint32_t));
+ SHA256_Final(hashes.fuses_hash, &sha256);
+
+ fprintf(stderr, "fuses hash:");
+ for (size_t i = 0; i < sizeof(hashes.fuses_hash); ++i) {
+ fprintf(stderr, "%02x", hashes.fuses_hash[i]);
+ }
+ fprintf(stderr, "\n");
+
+ result = key.sign(&hashes, sizeof(hashes), &sig);
+
+ if (result != 1) {
+ fprintf(stderr, "key.sign:%d\n", result);
+ } else {
+ size_t rwords = key.rwords();
+ key.toArray(hdr->signature, rwords, sig);
+ }
+
+ if (sig) BN_free(sig);
+
+ return result == 1;
+}
diff --git a/util/signer/publickey.cc b/util/signer/publickey.cc
index 6dd99dd146..51499ab52f 100644
--- a/util/signer/publickey.cc
+++ b/util/signer/publickey.cc
@@ -1,11 +1,10 @@
-//
-// 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"
+/* 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 <common/publickey.h>
+#include <string.h>
#include <string>
#include <openssl/bn.h>
@@ -14,50 +13,64 @@
#include <openssl/pem.h>
#include <openssl/rand.h>
-PublicKey::PublicKey(const char* filename) {
+#include <common/gnubby.h>
+
+PublicKey::PublicKey(const std::string& filename) : key_(NULL), publicOnly_(true) {
EVP_PKEY* pkey = NULL;
BIO* bio = BIO_new(BIO_s_file());
- if (BIO_read_filename(bio, filename) == 1) {
+ OpenSSL_add_all_ciphers(); // needed to decrypt PEM.
+ if (BIO_read_filename(bio, filename.c_str()) == 1) {
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+
+ if (pkey) {
+ publicOnly_ = false;
+ } else {
+ // Try read as public key.
+ (void)BIO_reset(bio);
+ pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
+ if (pkey) {
+ fprintf(stderr, "read public key only, assuming gnubby for signing..\n");
+ }
+ }
}
- if (NULL == pkey) {
- fprintf(stderr, "loadKey: failed to load RSA key from '%s'",
- filename);
+ if (!pkey) {
+ fprintf(stderr, "loadKey: failed to load RSA key from '%s'\n",
+ filename.c_str());
}
BIO_free_all(bio);
- key = pkey;
+ key_ = pkey;
}
PublicKey::~PublicKey() {
- if (key) {
- EVP_PKEY_free(key);
- key = NULL;
+ if (key_) {
+ EVP_PKEY_free(key_);
+ key_ = NULL;
}
}
bool PublicKey::ok() {
- return key != NULL;
+ return key_ != NULL;
}
size_t PublicKey::nwords() {
- RSA* rsa = EVP_PKEY_get1_RSA(key);
+ 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);
+ 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);
+ RSA* rsa = EVP_PKEY_get1_RSA(key_);
BN_CTX* ctx = BN_CTX_new();
BIGNUM* r = BN_new();
BIGNUM* rem = BN_new();
@@ -77,7 +90,6 @@ uint32_t PublicKey::n0inv() {
return result;
}
-/*static*/
void PublicKey::print(const char* tag, size_t nwords, BIGNUM* n) {
BN_CTX* ctx = BN_CTX_new();
BIGNUM* N = BN_new();
@@ -88,7 +100,8 @@ void PublicKey::print(const char* tag, size_t nwords, BIGNUM* n) {
BN_set_bit(r, 32); // 2^32
BN_copy(N, n);
- printf("const uint32_t %s[%lu] = {", tag, nwords);
+ printf("const uint32_t %s[%lu + 1] = {", tag, nwords);
+ printf("0x%08x, ", n0inv());
for (size_t i = 0; i < nwords; ++i) {
if (i) printf(", ");
BN_div(N, rem, N, r, ctx);
@@ -103,7 +116,6 @@ void PublicKey::print(const char* tag, size_t nwords, BIGNUM* 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);
@@ -112,32 +124,8 @@ void PublicKey::print(const char* tag, size_t nwords,
}
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* rsa = EVP_PKEY_get1_RSA(key_);
+ print(tag, rwords(), rsa->n);
RSA_free(rsa);
}
@@ -165,7 +153,7 @@ void PublicKey::toArray(uint32_t* dst, size_t nwords, BIGNUM* n) {
}
int PublicKey::encrypt(uint8_t* msg, int msglen, uint8_t* out) {
- RSA* rsa = EVP_PKEY_get1_RSA(key);
+ RSA* rsa = EVP_PKEY_get1_RSA(key_);
int result =
RSA_public_encrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING);
RSA_free(rsa);
@@ -173,7 +161,7 @@ int PublicKey::encrypt(uint8_t* msg, int msglen, uint8_t* out) {
}
int PublicKey::decrypt(uint8_t* msg, int msglen, uint8_t* out) {
- RSA* rsa = EVP_PKEY_get1_RSA(key);
+ RSA* rsa = EVP_PKEY_get1_RSA(key_);
int result =
RSA_private_decrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING);
RSA_free(rsa);
@@ -182,7 +170,7 @@ int PublicKey::decrypt(uint8_t* msg, int msglen, uint8_t* out) {
int PublicKey::raw(uint8_t* in, int inlen, BIGNUM** out) {
- RSA* rsa = EVP_PKEY_get1_RSA(key);
+ RSA* rsa = EVP_PKEY_get1_RSA(key_);
BN_CTX* ctx = BN_CTX_new();
BIGNUM* m = BN_new();
BIGNUM* r = BN_new();
@@ -210,7 +198,7 @@ int PublicKey::sign(const void* msg, size_t msglen, BIGNUM** output) {
uint8_t* sig = NULL;
unsigned int siglen = 0;
- unsigned int tmplen = EVP_PKEY_size(key);
+ unsigned int tmplen = EVP_PKEY_size(key_);
ctx = EVP_MD_CTX_create();
if (!ctx) goto __fail;
@@ -220,14 +208,29 @@ int PublicKey::sign(const void* msg, size_t msglen, BIGNUM** output) {
if (EVP_DigestUpdate(ctx, msg, msglen) != 1) goto __fail;
sig = (uint8_t*)malloc(tmplen);
- result = EVP_SignFinal(ctx, sig, &siglen, key);
+
+ if (publicOnly_) {
+ fprintf(stderr, "gnubby signing..");
+ fflush(stderr);
+ // TODO: 2k -> gnubby, 3k -> HSM?
+
+ Gnubby gnubby;
+ result = gnubby.Sign(ctx, sig, &siglen, key_);
+ fprintf(stderr, "gnubby.Sign: %d\n", result);
+ } else {
+ fprintf(stderr, "ossl signing..");
+ fflush(stderr);
+ result = EVP_SignFinal(ctx, sig, &siglen, key_);
+ fprintf(stderr, "EVP_SignFinal: %d\n", result);
+ }
+
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;
+ rsa = EVP_PKEY_get1_RSA(key_);
+ if (BN_lshift(tmp, tmp, rwords() * 32) != 1) goto __fail;
bnctx = BN_CTX_new();
if (BN_mod(tmp, tmp, rsa->n, bnctx) != 1) goto __fail;
diff --git a/util/signer/publickey.h b/util/signer/publickey.h
deleted file mode 100644
index 0a33460433..0000000000
--- a/util/signer/publickey.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// 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-A.pem b/util/signer/rom-testkey-A.pem
new file mode 100644
index 0000000000..926b6b0497
--- /dev/null
+++ b/util/signer/rom-testkey-A.pem
@@ -0,0 +1,364 @@
+# output of: openssl genrsa -f4 3072
+#-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -f4 2048
+#-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxkF2U+bC+B6BCfzfM9p3g6J4HbMvdhV3xS1HJSpCdsqhQMkO
+6l08LV1R4U7hjF1Nk/7C546XKDJegf9hhHuSZkYtXZOOBdYP8DV8ckrB3Lz6WF+N
+54WJTPUaFFRN6ynedl6oRa7vdDW5cjOYGwIqkZg24uPAJKzmKYPkX8BV7d1rNF7+
+LkuDGCt9SzJON7459JMC/mAy1pFS99SPmkmH31OG+WQGq8puqdUdnzT50AUznHOS
+5QJiMY1kTXp2iOkII+pzuPhpwjxtqIH4riJ/Rs8lHBNSL/MrK8jsGj0njWPLi0D5
+lSIFmznYn29WayUCnEvjTOAJaUBYwlQrh8eDbwIDAQABAoIBAGdyON/GhO0aXZJs
+k7pmv/27hJlaqeUfhoMoAPtvdYaubD11q4a2Z3P0QYkis6Wd+aDDScexK0YaIh4K
+t0N9hwI+k+VD3NNGwsI/5lNsloMrSCVclpq7nsy9B4KcCpwuGJoDAiJ6CQ/GCKGC
+MlfFZZjap6jz1YpNfTskSsF7hlq6RceOXas4bx8YhZl4V+EkDez3nT6hHT9PUal9
+IN+wGV0xYShSUhRSp9qdm/a8WiyETxjPkh8p+5jVpJTb61XbkIzzSnsl+Xmukapk
+fJO6Rcbp/V/78zV5YuR0hbENexPEf/R8iEuNxtVAz7G09U+8t1fTIuCFYy2oLh43
+rAwjHaECgYEA+Ba+UkzoNP0oFnMj4hJgE31NmnPWBMnjyGgchoPYQZVf+tjyCOj9
+bc0D1LQzG8UrkAxMnm4VWUniJ6CzaLHNPKiUvPeS+z1ofvsMup8ly23yL4q3QKOy
+nfHQREW5wHHldvwc2uZ53bUInOEPUpQMA9UPFgbighx6WQVXyszAP4MCgYEAzJPo
+zL+Ix1s/fwZOjfkFCnWsKcJ1m8t7vEObke4ovWaHP6JpHAumys1ih2F3qg0jKCa5
+yItv/l+Hj9fssYlR09zOmzAR0g8GGFqVgDxFyX0NrXmxcqYexVwNmd0scQKFwj2J
+iazYOs1AMuQZ1MqYaw4wS2eG28/Y/F3OooVu3KUCgYEAqyr9+0imudCk9QBSFKu6
+8Bd3EIa1di5ZY685ZzHWZkGKrEc8jxs5p6VY7Eu8K3/Zc5SY1IJ9ZlKMn+zHfAeY
+5C4oGUzzczbGPz41ZRli+T2NaHHbx9Rp64GowUIeTAIJYRPHUNzN3kMGgz7g1Ifi
+1k4ND5SGeWDupTcDgJ/OUMMCgYBCK71rLLDDOuKI5bNW/KsCvRkY0MtVvzWgqYWS
+aCRJTvaEQLYa2jHy+wfZnk8kc+dhP1VsZp2s+51Pi5oFutL64jr5u0yoUy+3hOVC
+ezxfddGMwQYCfXUKhUHo+L89NoGpWFo6a+vs6SLQ0zL/vyAZ0JcSbMQUKWCYEIeb
+zekT+QKBgCrI1XeYP0+PtKVVsE+0CmKSNhi2jKtCgyFCzHQJb2NHz25fZhlo7aM0
+oLgdsQFqK1+oALk8fT0Bg1jddsggbG7iZlieBTC5tPkNTMjpTcBBgnI5BkbZYuNi
+qqbFnV0S2cP86BWzHXrtXWvmdesvMBRGxO7wG/ELXI10rHZG7DvH
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -3 2048
+#-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA0/22CLHy3W8TJcHX/yMQOFIyARFxmlsz9wxilz9oCjEpDpld
+GIsS0vXmv3U3hqgD0+ocFwKeYKe86JBTp0KsD6rmLvRwMfF1/n8eLsvXSWU7Z3hF
+d6KFUvZ1t4G9WxFq5V2k7pumRhN2g2J0oVPrJpBlJKbOG7f0L6+IZT+V00CQrqUJ
+kVxHBCxXosxkJPmoU8NBG+Nlv0OncIj2HrfnzAxmbPs/6uM/yeiTM462M/zqXu44
+l16K17uSGJ5/s6CCVK0AuDcO7nZ+T8gZRXrZw6Ib2t3VgqA38fyJLOZJ2Vc3t7KO
+05fM5hNP18JzMpmpd1avhzHgDEer45qsAt1jaQIBAwKCAQEAjVPOsHah6PS3boE6
+qhdgJYwhVguhEZIipLLsZNTwBstwtGY+EFy3N06Z1PjPrxqtN/FoD1cUQG/TRbWN
+GixytRyZdKL1dqD5VFS+yd06MO4nmlAuT8GuN075JQEo52Dx7j5t9GfELrekV5b4
+a41HbwruGG80EnqiynUFmNUOjNR/ETxMy4pzd8XH9RPEh9UfRQcrDsU7KOsQVygB
+KaU4OhuJMeaU7K+75thaAIFTM5Cp7SUu54Ri8wXcnuvW3qAI4BM/KMnbPWktNHAg
+6WkASh3oaY/J03wlfW0J3RBEpefW4dJ8sMq4qAXZ3ycgfJE5lTI8oJH3nX94cOla
+FJ0bqwKBgQDvOOwWTw2xtoOYohKYw/vvIMMf8vx4AaG68Hc4cBsGJiFxuWJTT4nV
+c67RuVUArObDMe/KgGMy5wqglyDgojXMVhRtOTMBP5tSaUsNViWKJJ/u+QyTFvu4
+q+o2/OuQkYy1WZ/U9rJvb6LcvOuNaffV0JShXJ4HnRLuO5irD60kWQKBgQDi296A
+EP7oGgATERwklT4KS3Vgkr8VAEFT/VW78CUNTsGm6L8NOFHQe/U6eXe4ub0oSUan
+u7TDhCgmkxvcw3qornu0wdVE0r1oF9TakTfPL9VQQ3mTrmpHCe7DZGJSTu7AC1b+
+07VIemesTBw1Dcf9RvazObjlAvWI/qP51ESVkQKBgQCfe0gO3152ea0QbAxl1/1K
+Fddqof2lVmvR9aTQSryuxBZL0Ow3ilvjonSL0ONVyJnXdp/cVZd3RLHAZMCVwXky
+5A2eJiIA1RI28NyzjsOxbb/0pghiD1J7HUbPU0e1tl3OO7/jTyGfn8HoffJeRqU5
+NbhrkxQFE2H0J7sctR4YOwKBgQCXPT8AC1SavAAMthLDDilcMk5AYdS4qtY3/jkn
+9W4I3yvEmyoI0DaK/U4m+6Ul0SjFhi8afSMtAsVvDL0916cbHv0jK+ODNyjwD+M8
+YM/fdTjgLPu3yZwvW/SCQuw230nVXOSp4nja/EUdiBLOCS/+L08iJntDV05bVG1R
+OC25CwKBgESVOTLIZfSkAP8GtolQt5meiQNZAM1fkbTZmydzPU8RcxYSjH4S7xzZ
+wqMvedczh7eJ+CjUbYN+n3jj6Dual/36WHsPntV2avejb6UaInYd61t0murRuUsQ
+jZ6CAlsBSbhfOru6N3NQ6tQC5+9hPELCTIPgMU3vLFq1cP2G0fTT
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -3 3072
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -f4 3071
+#-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -3 2047
+#-----BEGIN RSA PRIVATE KEY-----
+MIIEoAIBAAKCAQBd5Az6u/daXYeCv3aHYGJ01Aw8NUu8YLoT9ynBDvm7hT5yWLbY
+ugLSIN44gLSsgVjJ7g6n0LddheDaOC0YRPgyYcipg8/Lo8Doe8x8qerRz+PwewuP
+k9mDmTPvHANJJJ7d7crlwv/OhklPEmDaSUrAVh5AmlUYNxRwbOC/Di3SLzJc/X7m
+7ItclQouinH+dOiQGUZwyLGzE+TBB7io3NyV74yxHnmpwyxussNqKcfiN8C1dcIb
+uqs/IEoR/TZ5EAcJI8XbSTe8SOVwWIEUcBBohzQWUWJbzwcc2CCijITrnsrO+/Cc
+FD34krjss2nDtA2mZsq5b/IqmofziTyf+B9DAgEDAoIBAD6YCKcn+jw+WlcqTwTq
+7E3istLOMn2V0WKkxoC0pn0DfvblzzsmrIwV6XsAeHMA5dv0CcU1z5OulebQHhAt
++sxBMHECiofCgJr9Mv3GnIvf7Ur8sl+35le7d/S9V4YYaelJMe6B/98EMN9hlebb
+hyrkFCsRjhAkuErzQH9eyTbJRB/7Fqi5pU5WWz7nuhIrrYVMeTEqbEdvCNiLjfrv
+ooH7Eq2tQEVDcf49bCRhzOSpaykRsw9UkcLjWFn2OSnWrDlVSB9mbdGNJpBHLThf
+WMuac2OxxbCl89bAFQ/oVjAqxLFzn46C7JZvLe/p8cHsYqbsRC21abbVDuAhBYX7
+nQsCgYEA4amkQZxDK1JCEZ7CcvFiV7SgkWqOesjDObpDd9te7M6cjDDVQQf560NR
+6Mp4nLe8yBc00maPpLeL+FvovYtDRbDb2yIhyH9WgRWC30vtCMY/GpgS4jOcr3e0
+K9TkTbZTIINvem56rPOVJpJX5yXoG8lQdg/61TdZYISjSE123ykCgYBqg2CbTZLo
+FNFvsWxn8dEMk4X/PHqUfyodwfLr1RZ8BFpke8VYOeQWLgS4aR5303zX4KZjHiwr
+4z9NLyMjLwq/Akf+ig3Kgp6qgmrd8/QCcOBsaLPXnzlp4wPVNcu17QuDQV8G19Uu
+I1bNcnyX+uld4rPuZOT+YQGRHrRsCYfUiwKBgQCWcRgrvYIc4YFhFIGh9kGPzcBg
+8bRR2yzRJtelPOnzNGhddeOAr/vyLOFF3FBoen3auiM27wptz7KlkpspB4Iudefn
+bBaFqjmrY6yU3UiwhCoRurdBd73KT81yje2JJDdrAkpRnvxzTQ4ZtuVEw/AShjWk
+Cqc4z5DrAxeFiPnqGwKBgEcCQGeJDJq4i5/LnZqhNghiWVTS/GL/cWkr90fjZFKt
+kZhSg5AmmA7JWHrwvvqM/eVAbuy+yB1CKjN0whd0sdSsL/8Gs9xXFHGsRz6ioqxL
+QEhFzTpqJkaXV+N5MnlIsleA6gSP43QXjzOhqGVR8OlBzUmYmKmWAQtpzZ1br+MH
+AoGBAJAgNpv0p3hZly5oecTzIan03/nJ7HlhS9XsR6zyI3n/HPxEd4MW7nAMlend
+FtuJ8u033ZtWDW8E9qn6jrIMzgZgWMyh4Az+xYdq+ZMT6c3q/Ujh50FSeFc8y1we
+onrGgK3d/IXnGIUpUUIexw6mEx4eux1GHusgMA/bWRG/DQeQ
+-----END RSA PRIVATE KEY-----
+# output of: openssl genrsa -3 3056
+#-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+# openssl genrsa -3 3071
+#-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
+#openssl genrsa -3 1023
+#-----BEGIN RSA PRIVATE KEY-----
+MIICWQIBAAKBgGgVIcckiflOrlrsuagONesnm4qhEf8TvWvwZzTUj6IvtFIsXgFF
+Ql/eY3u+By1xFlA3riLMwxXtCvYM2qzXmgu8GH8Mru9Ikon5xapXgR4JNdUDqhgS
+nMTfO/7/6qBOGqyMVjJSBSPftKUGa9QGOxQnG0G/FVHQ1mKx299FW707AgEDAoGA
+RWNr2hhb+4nJkfMmcAl5R2+9BxYL/2J+R/WaIzhfwXUi4XLpVi4sP+mXp9QEyPYO
+4CUewd3XY/NcpAiRyI+8BukbhUdeGdQKf3a6VN5V2r2PlUy9lbOIfa4EfQM64QWF
+/BvIq+tVFg3lvAylpxJurmEi7Y7c4uC6ksseSG9YSusCQQDu50smOMGfr5jxccfq
+R9nYEqnfWI7nquwyA4eiHnbWVEDGNREY39G1zh+lYRqiQSk1whpsslU68VoyElwO
+3VVxAkBvh+v7aQbq0zHWPGMfuHwUy8sxNSidpRwoMbvY89fvfXGcdB9YJbEVDetO
+Ej7IU+VfpML8F6hEzSxPHBaPefdrAkEAn0TcxCXWanUQoPaFRtqROrcb6jsJ78dI
+IVevwWmkjuLV2Xi2EJU2eTQVGOtnFtYbeSwRncw40fY8IWGStJOOSwJASlqdUkYE
+nIzL5ChCFSWoDd0yIM4bE8NoGsvSkKKP9P5LvaK/kBkguLPyNAwp2uKY6m3XUrpw
+Ld4dihK5tPv6RwJBALFMpNtx+z1qo5ViOA083dV4HvBUnsaOObmm+fF5di0mNO50
+tHepBmEvmMmbhbGtj4AuWYMgzitlQFRG13plpYc=
+-----END RSA PRIVATE KEY-----
+#openssl genrsa -f4 1023
+#-----BEGIN RSA PRIVATE KEY-----
+MIICWgIBAAKBgF30c14/57rbnRAHchaeO5PW9ub2bzKzoNYsaU50wegI8yFmITTh
+nzGahCNtipdmiD2Rh2By+Kknqtc7Rdpqsl63DY2qkTqab3nnyZlGvl1/alz59l8S
+JgBlrQYeOkjuixYusUtIedUiBMDffRnJFTfISUTOeoVjqd1R6XapitcxAgMBAAEC
+gYAm8JuoImBOh5rO++cDDM2wdlWaX+SyZNRyL7HtfGKpYpwx6Xw1yJUMH/hIHqxk
+F3C748bx6HS61t/tmHlZDwOhy+c3kjKtjLuYoJZXfHlduPprRkGFlzD/tTLqOPIO
+Ml+hfpIUIo2pKoz0KiD9N7izNnUX280V5v7woOAj0u5gyQJBAM6a1I36rigZoxHi
+EZgzyYvJCR6Fq0FBpoTwfHXtXc0LQKNz761Q9AyPU3tt0HDHu67M18eKDzSgCGeD
+4M6lV2sCQHRq7JieKpVsRs1Rj07tQU+Fnaq5hVgnLymHXiJoQkUNRBPeFX9bqdU3
+nKMjq1uG2t2ihLtXqt5L522MA36f3tMCQQC08lmyt5iMOlfds7dhLAoayUmZV8yz
+bIIg8FaxGeELUg2cmk+R5dK4pVaiRX1HKUcxQurBMiM7HeX+czwTADBdAkBhKBMI
+U4eqaFC2bUiyT+LfoN0ya93S8fC0Mdrn0CbuReH4yrEaNr+Hx/+bTttlaSgjK8ym
+JMXe+23KyLzp4jYtAkBpLWQxiVQndB3eG1yur8fPku4KjsBcnTy2qz3b7OCwX51q
+CWgGl6TiZiSp69UV1wwZS6tSGwyQBdDcer2aNeoZ
+-----END RSA PRIVATE KEY-----
+#openssl genrsa -f4 1008
+#-----BEGIN RSA PRIVATE KEY-----
+MIICUQIBAAJ/ALIt6Zuh8xmqJEG1Xcyfjf5hYdrH4G37zqHKnPqFBA8wCqyURBrV
+qG9ykJh02ebbODHTcp8fG/uDOFtuIuQB9wdxD66Q6UpfJMsn+nYWUyIyFeDFLwZY
+WGeisDo5Xr6768KlFPbFbqsXSjZobpmq0Z1V05RNfwGH/byWLZu+FQIDAQABAn4Y
+Y9aFek63paWe5P+5AeJC/vuRIikjhQOM/Bou5MRge9gKNaV48uRClTEb3WrygfGY
+SHR5OZ3v+M9fHJ1kDqySd5sIblrIXwTPor9Pc9ktluSdqJqoe040HtK/T9/ZMMfR
+l5/zj6WMrYOn+/FQ9WoGMk0Lvl2xa/GH2PKPJ+UCQADbMqF5o4dZvIhzalZuTv4y
+FanJgH6j5njD0ubBMNm2VcCek/mdzyLjcbCAS8zHSOPjwB3K71D8URbDbinsmH8C
+QADQGENYErO7rWRr189mZpqcf1LSCGHvgFJt/cNOGMfkSFkESwx5iJlVIOWMAOoq
+FSM95hcYLHT3yMDMK1dvf2sCP3+MQ6fbPyw5Z5D5RJflgUa1klm9WnOW7ZjNRRcP
+jO/rkCz2YB6y5FNC2XieIueng2wIY42tmgtoR/2qQdz5kwI/HqDTmKz4A1GcDDtx
+bVMAwrQCzqMpNwUeHM5S5rXDBhiX8YfSSDkQyHTVFGw2CwedbkjlcUtp0+aaafKf
+JewNAkAAlwvn0zsJWKNuuyaj5s+m+C5uMtVLUsI2HLshiNPCSPo6HAMB5K2JR2L7
+VKzrB1eum6WOioA8BPDfZ7kN/A68
+-----END RSA PRIVATE KEY-----
+#openssl genrsa -f4 1010
+#-----BEGIN RSA PRIVATE KEY-----
+MIICUwIBAAJ/Ax7mPNavitMKXqMYarOJG+6kkCYYuafedhjJ09cmnjm0djTiHWbD
+em6nOesz4hWOR4/aSrxTCYYV6A7MtmzmkRotLf9zqs8gjktkkKdPDgKk4Rf+mNCG
+xjIjZVKvdHzS+ak+/vO5r43hYOtA9o+iNrDUgbR3vyi6W2RRRFnFXQIDAQABAn4C
+Sq7J/t7rFBCJbzx/H1mRKa+wLEn8DUtRbuLIrzes+rOMG0nyTYVAZAJqhk+TuEDE
+TOACAkr4s39i63tqfI7TZ/hBdPmHWNo6C84yaxb4/2rK+TwXoP1dyhCPY/p3BmYe
+TmnUVCvWuf0ozwTarWhFByDn7iLucawEaj4dHAECQAHPm57vVXHOzNmt6NfuwpMW
+2xrp2EDHsz5OI9jBqEVPUXQAgB1lRDtYfvEhQOfTZSREikbkc3BX/FOFdkFG0J0C
+QAG5JRVKW4TRgpVvJD3c7KDt+xSgfsPLcCIQWPITBKhdWdZmqOi3TxkdG8iTSgxs
+51tcxN9/nl5diahu15GSy8ECQAGJa3eZhLn6k45abdDotcfayn7xWOnITkyeRP9J
+gAGpSOcXEaFgcWGnPLplzM2ucBMx8uIsNeZtVByQkoTckmUCQAFetfKVKOkQvyYa
+M8/SwXS3ffNKHT1/kOgj4vKg2AUaIf/nocVRZlrq31m4bxgtxEjIGeQtSzRuXLBB
+XrPm5oECQAHJfnVFzFGpwHfuWkd+zC86m35d1WIuETVkb0TzoR3+rFELpPzQdoSC
+bsu7Mgb1HSqoU852iHPqi421I7jWcP0=
+-----END RSA PRIVATE KEY-----
+#openssl genrsa -f4 1022
+#-----BEGIN RSA PRIVATE KEY-----
+MIICWAIBAAKBgC5hrCpkwMOAyUpUWgrDFRP0r4tjSoU5jGieHYWxMGrlhVo2VPU2
+2Vddq0rKDgKrRMUv8h02DKhy/4iyZV/vS5sOFDx2DhvzBxuKWajL24xQ4kC64aFi
+qysHuCgBy6kmU1Fq6IZGQpaoNP2QEggYNyrLnm952us8hUr9wimriyXlAgMBAAEC
+gYAKARdZrZHGmajRV5B0cc/CFi0nK6uPQPKAPlqv06BV7lvtMXrxyXrn1VR1NVAA
+U2DqB4V5t4bTbrqfiZtELiFoiYGgZZ4xBcEkR53pJ8Hp7qO3OlUcxgrtg3emz4JT
+QKgBrKoOsPW62hEJwaZVVvvnsODNh0AilRLOginlVZVpAQJAcD7HeWBESfGAwcvC
+8mCpCuCzKC+4bW5M7+WmDzhlvkYhhXgr2ttAylY2uTw7h/Lu36C2Q5xm4c/PFUDr
+eEE/iQJAaciHQWCAhGQXgonTyC8V+InsEay151W44218dRcNdS0pug18RDTZRmbk
+oq1ldXEGhR5Zl1rhRZqd3VeG8E4gfQJAOERwUHseoIatCfkwJ7qm4uoTRYDujgTu
+EpBnt1eJQWH8qeC880BX8VZYpnD4UsVIU9hHpgsAVBCNedUKdUUYKQJAPlLa/XWx
+wk6GW26AzCyRsUDxSOiMs6MZr0TOUt4mG6dkMi3HzRel+4ibpAys/ih9QujCuB3f
+I3cZE1OCOQqOsQJABRFxEd8gZGVWAYiOPiMmHi3o9WxPye9+fGtX6n+KJivxj4vv
+F0oe4mpW6N88VEMOlrtLN2/6mHhmk2ddXJSJzg==
+-----END RSA PRIVATE KEY-----
diff --git a/util/signer/rom-testkey.pem b/util/signer/rom-testkey.pem
deleted file mode 100644
index 9cd8662c64..0000000000
--- a/util/signer/rom-testkey.pem
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# 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
deleted file mode 100644
index 2207a08d11..0000000000
--- a/util/signer/signed_header.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-// 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