diff --git a/chip/g/build.mk b/chip/g/build.mk index 331c5b085f..0c80af042d 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -4,8 +4,6 @@ # found in the LICENSE file. # -SIGNED_IMAGES = 1 - CORE:=cortex-m CFLAGS_CPU+=-march=armv7-m -mcpu=cortex-m3 @@ -126,7 +124,11 @@ endif MANIFEST := util/signer/ec_RW-manifest-dev.json CR50_RO_KEY ?= rom-testkey-A.pem -SIGNER = /usr/bin/cr50-codesigner +REAL_SIGNER = /usr/bin/cr50-codesigner +ifneq ($(wildcard $(REAL_SIGNER)),) +SIGNED_IMAGES = 1 +SIGNER := $(REAL_SIGNER) +endif ifeq ($(H1_DEVIDS),) CR50_RW_KEY = loader-testkey-A.pem diff --git a/util/signer/aes.cc b/util/signer/aes.cc deleted file mode 100644 index 4d120a102a..0000000000 --- a/util/signer/aes.cc +++ /dev/null @@ -1,51 +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. - */ -#include - -#include - -#include -#include -#include -#include - -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(in), - reinterpret_cast(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(in), - reinterpret_cast(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(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(out), &out_len); - CMAC_CTX_free(cmac); -} diff --git a/util/signer/codesigner.cc b/util/signer/codesigner.cc deleted file mode 100644 index 89b23eccd2..0000000000 --- a/util/signer/codesigner.cc +++ /dev/null @@ -1,601 +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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef HAVE_JSON -#include -#else -#include -#endif - -#include -#include -#include - -#include -#include -#include - -#include -#include - -using namespace std; - -#define VERBOSE(...) \ - do { \ - if (FLAGS_verbose) fprintf(stderr, __VA_ARGS__); \ - } while (0) -#define FATAL(...) \ - do { \ - fprintf(stderr, __VA_ARGS__); \ - abort(); \ - } while (0) - -bool FLAGS_verbose = false; -bool FLAGS_cros = false; -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; - - for (cur_node = node->children; cur_node; cur_node = cur_node->next) { - if (!strcmp("HashItem", (const char*)(cur_node->name))) { - // Hardcode parse keyval - 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); - - if (!strcmp(key, (const char*)keyName)) { - // Found our key, save val and done. - xmlFree(keyName); - val = valData; - break; - } - - xmlFree(valData); - xmlFree(keyName); - } - - val = get_val(cur_node, key); - if (val) { - // Found our key somewhere deeper down; done. - break; - } - } - - return val; -} - -static bool get_fuse(xmlNodePtr a_node, map* ids, - map* bits) { - bool result = false; - - // Interested in - if (strcmp("HashType", (const char*)(a_node->name))) { - return result; - } - - // 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"); - - // 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; - } - - xmlFree(FuseLogicalOffset); - xmlFree(Width); - xmlFree(RegName); - - return result; -} - -static bool find_fuses(xmlNodePtr a_node, map* ids, - map* 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 = get_fuse(a_node->parent->parent->parent, ids, bits); - } - } - - if (content) xmlFree(content); - - if (!done && cur_node->children) { - done = find_fuses(cur_node->children, ids, bits); - } - } - - return done; -} - -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 (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); - } - } - - return done; -} - -// Read XML, populate two maps, name -> val -bool readXML(const string& filename, map* ids, - map* 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); - } - - xmlCleanupParser(); - xmlMemoryDump(); - - return result; -} - -// Read JSON, populate map, name -> val -bool readJSON(const string& filename, string* tag, - map* values, map* fusemap, - map* infomap) { - bool result = false; -#ifdef HAVE_JSON - ifstream ifs(filename.c_str()); - if (ifs) { - // Touch up a bit to allow for comments. - // Beware: we drop everything past and including '//' from any line. - // Thus '//' cannot be substring of any value.. - string s; - while (ifs) { - string line; - getline(ifs, line); - size_t slash = line.find("//"); - if (slash != string::npos) { - line.erase(slash); - } - s.append(line); - } - - // Try parse. - rapidjson::Document d; - if (d.Parse(s.c_str()).HasParseError()) { - FATAL("JSON %s[%lu]: parse error\n", filename.c_str(), - d.GetErrorOffset()); - } else { -#define CHECKVALUE(x) \ - do { \ - if (!d.HasMember(x)) { \ - FATAL("manifest is lacking field '%s'\n", x); \ - }; \ - } while (0) - -#define GETVALUE(x) \ - do { \ - if (!d.HasMember(x)) { \ - FATAL("manifest is lacking field '%s'\n", x); \ - }; \ - (*values)[x] = d[x].GetInt(); \ - } while (0) - - CHECKVALUE("fuses"); - const rapidjson::Document::ValueType& fuses = d["fuses"]; - for (rapidjson::Value::ConstMemberIterator it = fuses.MemberBegin(); - it != fuses.MemberEnd(); ++it) { - (*fusemap)[it->name.GetString()] = it->value.GetInt(); - } - - CHECKVALUE("info"); - const rapidjson::Document::ValueType& infos = d["info"]; - for (rapidjson::Value::ConstMemberIterator it = infos.MemberBegin(); - it != infos.MemberEnd(); ++it) { - (*infomap)[it->name.GetString()] = it->value.GetInt(); - } - - GETVALUE("keyid"); - GETVALUE("p4cl"); - GETVALUE("epoch"); - GETVALUE("major"); - GETVALUE("minor"); - GETVALUE("applysec"); - GETVALUE("config1"); - GETVALUE("err_response"); - GETVALUE("expect_response"); - GETVALUE("timestamp"); - - CHECKVALUE("tag"); - const rapidjson::Document::ValueType& Tag = d["tag"]; - tag->assign(Tag.GetString()); - - result = true; - -#undef GETVALUE -#undef CHECKVALUE - } - } -#endif // HAVE_JSON - return result; -} - -string inputFilename; -string outputFilename; -string keyFilename; -string xmlFilename; -string jsonFilename; -string outputFormat; -string signatureFilename; -string hashesFilename; -bool fillPattern = false; -uint32_t pattern = -1; -bool fillRandom = false; - -void usage(int argc, char* argv[]) { - fprintf(stderr, - "Usage: %s options\n" - "--input=$elf-filename\n" - "--output=output-filename\n" - "--key=$pem-filename\n" - "[--b] ignored option, could be included for forward compatibility\n" - "[--cros] to sign for the ChromeOS realm w/o manifest\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" - "[--signature=$sig-filename] replace signature with file content\n" - "[--hashes=$hashes-filename] destination file for intermediary " - "hashes to be signed\n" - "[--randomfill] to pad image to 512K with random bits\n" - "[--patternfill=N] to pad image to 512K with pattern N\n" - "[--verbose]\n", - argv[0]); -} - -int getOptions(int argc, char* argv[]) { - static struct option long_options[] = { - // name, has_arg - {"b", no_argument, NULL, 'b'}, - {"cros", no_argument, NULL, 'c'}, - {"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'}, - {"verbose", no_argument, NULL, 'v'}, - {"xml", required_argument, NULL, 'x'}, - {"signature", required_argument, NULL, 's'}, - {"hashes", required_argument, NULL, 'H'}, - {"randomfill", no_argument, NULL, 'r'}, - {"patternfill", required_argument, NULL, 'p'}, - {"writefuses", required_argument, NULL, 'w'}, - {0, 0, 0, 0}}; - int c, option_index = 0; - outputFormat.assign("hex"); - while ((c = getopt_long(argc, argv, "i:o:p:k:x:j:f:s:H:bchvr", 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 'b': - break; - case 'c': - FLAGS_cros = true; - 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 's': - signatureFilename.assign(optarg); - break; - case 'j': - jsonFilename.assign(optarg); - break; - case 'f': - outputFormat.assign(optarg); - break; - case 'H': - hashesFilename.assign(optarg); - break; - case 'r': - fillRandom = true; - break; - case 'p': - fillPattern = true; - pattern = strtoul(optarg, NULL, 0); - break; - case 'h': - usage(argc, argv); - return 1; - - case 'v': - FLAGS_verbose = true; - break; - 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 (getOptions(argc, argv)) { - exit(1); - } - - PublicKey key(keyFilename); - if (!key.ok()) return -1; - - // Load elf. - Image image; - if (!image.fromElf(inputFilename)) return -2; - - if (fillPattern) image.fillPattern(pattern); - if (fillRandom) image.fillRandom(); - - SignedHeader hdr; - - 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() + - 12; // TODO: m3 instruction prefetch sets off GLOBALSEC when too tight - // make sure these are nops or such? - hdr.timestamp_ = time(NULL); - - // Parse signing manifest. - map values; - map fuses; - map infos; - string tag; - - if (jsonFilename.empty()) { - // Defaults, in case no JSON - values.insert(make_pair("keyid", key.n0inv())); - values.insert(make_pair("epoch", MANIFEST_EPOCH)); - values.insert(make_pair("major", MANIFEST_MAJOR)); - values.insert(make_pair("minor", MANIFEST_MINOR)); - } - - // Hardcoded expectation. Can be overwritten in JSON w/ new explicit value. - fuses["FW_DEFINED_DATA_EXTRA_BLK6"] = 0; - - if (!jsonFilename.empty() && - !readJSON(jsonFilename, &tag, &values, &fuses, &infos)) { - FATAL("Failed to read JSON from '%s'\n", jsonFilename.c_str()); - } - - // Fill in more of hdr, per manifest values - for (map::const_iterator it = values.begin(); - it != values.end(); ++it) { - VERBOSE("%s : %u\n", it->first.c_str(), it->second); - } - - hdr.p4cl_ = values["p4cl"]; - hdr.epoch_ = values["epoch"]; - hdr.major_ = values["major"]; - hdr.minor_ = values["minor"]; - hdr.applysec_ = values["applysec"]; - hdr.config1_ = values["config1"]; - hdr.err_response_ = values["err_response"]; - hdr.expect_response_ = values["expect_response"]; - if (values["timestamp"]) hdr.timestamp_ = values["timestamp"]; - - VERBOSE("timestamp: %ld\n", hdr.timestamp_); - - // Check keyId. - if (values["keyid"] != hdr.keyid) { - FATAL("mismatched keyid JSON %d vs. key %d\n", values["keyid"], hdr.keyid); - } - - if (FLAGS_cros) { - if (!tag.empty()) { - FATAL("--cros whilst also specifying tag per manifest is a no go"); - } - tag = "\x01\x00\x00\x00"; // cros realm identifier in rwr[0] - } - - // Fill in tag. - VERBOSE("tag: \"%s\"\n", tag.c_str()); - strncpy((char*)(&hdr.tag), tag.c_str(), sizeof(hdr.tag)); - - // List the specific fuses and values. - VERBOSE("care about %lu fuses:\n", fuses.size()); - for (map::const_iterator it = fuses.begin(); - it != fuses.end(); ++it) { - VERBOSE("fuse '%s' should have value %u\n", it->first.c_str(), it->second); - } - - // Parse xml. - map fuse_ids; - map fuse_bits; - uint32_t xml_p4cl = 0; - - if (!xmlFilename.empty() && - !readXML(xmlFilename, &fuse_ids, &fuse_bits, &xml_p4cl)) { - FATAL("Failed to read XML from '%s'\n", xmlFilename.c_str()); - } - - if (values["p4cl"] != xml_p4cl) { - FATAL("mismatching p4cl: xml %u vs. json %u\n", xml_p4cl, values["p4cl"]); - } - - VERBOSE("found %lu fuse definitions\n", fuse_ids.size()); - assert(fuse_ids.size() < FUSE_MAX); - - if (fuse_ids.size() != 0) { - // Make sure FW_DEFINED_DATA_EXTRA_BLK6 is still at 125, width 3. - assert(fuse_ids["FW_DEFINED_DATA_EXTRA_BLK6"] == 125); - assert(fuse_bits["FW_DEFINED_DATA_EXTRA_BLK6"] == 5); - } - - // Whether we loaded xml or not, hardcode FW_DEFINED_DATA_EXTRA_BLK6 - fuse_ids["FW_DEFINED_DATA_EXTRA_BLK6"] = 125; - fuse_bits["FW_DEFINED_DATA_EXTRA_BLK6"] = 5; - - for (map::const_iterator it = fuse_ids.begin(); - it != fuse_ids.end(); ++it) { - VERBOSE("fuse '%s' at %u, width %u\n", it->first.c_str(), it->second, - fuse_bits[it->first]); - } - - // Compute fuse_values array, according to manifest and xml. - uint32_t fuse_values[FUSE_MAX]; - for (size_t i = 0; i < FUSE_MAX; ++i) fuse_values[i] = FUSE_IGNORE; - - for (map::const_iterator x = fuses.begin(); - x != fuses.end(); ++x) { - map::const_iterator it = fuse_ids.find(x->first); - if (it == fuse_ids.end()) { - FATAL("cannot find definition for fuse '%s'\n", x->first.c_str()); - } - uint32_t idx = it->second; - assert(idx < FUSE_MAX); - uint32_t mask = (1ul << fuse_bits[x->first]) - 1; - if ((x->second & mask) != x->second) { - FATAL("specified fuse value too large\n"); - } - uint32_t val = FUSE_PADDING & ~mask; - val |= x->second; - - fuse_values[idx] = val; - hdr.markFuse(idx); - } - - // Print out fuse hash input. - VERBOSE("expected fuse state:\n"); - for (size_t i = 0; i < FUSE_MAX; ++i) { - VERBOSE("%08x ", fuse_values[i]); - } - VERBOSE("\n"); - - // Compute info_values array, according to manifest. - uint32_t info_values[INFO_MAX]; - for (size_t i = 0; i < INFO_MAX; ++i) info_values[i] = INFO_IGNORE; - - for (map::const_iterator x = infos.begin(); - x != infos.end(); ++x) { - uint32_t index = atoi(x->first.c_str()); - assert(index < INFO_MAX); - - info_values[index] ^= x->second; - - hdr.markInfo(index); - } - - // TODO: read values from JSON or implement version logic here. - - // Print out info hash input. - VERBOSE("expected info state:\n"); - for (size_t i = 0; i < INFO_MAX; ++i) { - VERBOSE("%08x ", info_values[i]); - } - VERBOSE("\n"); - - if (!signatureFilename.empty()) { - int fd = ::open(signatureFilename.c_str(), O_RDONLY); - if (fd > 0) { - int n = ::read(fd, hdr.signature, sizeof(hdr.signature)); - ::close(fd); - - if (n != sizeof(hdr.signature)) - FATAL("cannot read from '%s'\n", signatureFilename.c_str()); - - VERBOSE("provided signature\n"); - } else { - FATAL("cannot open '%s'\n", signatureFilename.c_str()); - } - } - - // Sign image. - if (image.sign(key, &hdr, fuse_values, info_values, hashesFilename)) { - image.generate(outputFilename, outputFormat == "hex"); - } else { - FATAL("failed to sign\n"); - } - - return 0; -} diff --git a/util/signer/common/aes.h b/util/signer/common/aes.h deleted file mode 100644 index 08e4eefb50..0000000000 --- a/util/signer/common/aes.h +++ /dev/null @@ -1,24 +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_COMMON_AES_H -#define __EC_UTIL_SIGNER_COMMON_AES_H - -#include -#include - -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 deleted file mode 100644 index 52751491d7..0000000000 --- a/util/signer/common/ecdh.h +++ /dev/null @@ -1,24 +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_COMMON_ECDH_H -#define __EC_UTIL_SIGNER_COMMON_ECDH_H - -#include - -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 deleted file mode 100644 index 9c27303296..0000000000 --- a/util/signer/common/gnubby.h +++ /dev/null @@ -1,46 +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_COMMON_GNUBBY_H -#define __EC_UTIL_SIGNER_COMMON_GNUBBY_H - -#include -#include - -#include - -typedef struct env_md_ctx_st EVP_MD_CTX; -typedef struct evp_pkey_st EVP_PKEY; -typedef struct rsa_st RSA; -typedef struct bignum_st BIGNUM; - -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); - - int write(RSA* rsa); - - 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); - - int write_bn(uint8_t ins, BIGNUM* n, size_t length); - int doSign(EVP_MD_CTX* ctx, uint8_t* padded_req, uint8_t* signature, - uint32_t* siglen, EVP_PKEY* key); - // Open a gnubby, unspecified selection made when multiple plugged in. - int open(); - - libusb_context* ctx_; - libusb_device_handle* handle_; -}; - -#endif // __EC_UTIL_SIGNER_COMMON_GNUBBY_H diff --git a/util/signer/common/image.h b/util/signer/common/image.h deleted file mode 100644 index c7e04a2753..0000000000 --- a/util/signer/common/image.h +++ /dev/null @@ -1,61 +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_COMMON_IMAGE_H -#define __EC_UTIL_SIGNER_COMMON_IMAGE_H - -#include -#include -#include - -#include - -class PublicKey; -struct SignedHeader; - -class Image { - public: - Image(); - ~Image(); - - void randomize(); - - 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[], - const uint32_t info[], - const std::string& hashesFilename); - 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_; } - - void fillPattern(uint32_t); - void fillRandom(); - - 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 deleted file mode 100644 index 79c38a94c2..0000000000 --- a/util/signer/common/publickey.h +++ /dev/null @@ -1,70 +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_COMMON_PUBLICKEY_H -#define __EC_UTIL_SIGNER_COMMON_PUBLICKEY_H - -#include -#include - -#include - -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 96 (enough for upto 3071 moduli). - size_t rwords() const { return 96; } - - // # of significant words in modulus. - size_t nwords(); - - uint32_t public_exponent(); - - // -1 / least significant word of modulus. - uint32_t n0inv(); - - // PKCS1.5 SHA256 - // Montgomery 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); - - void modToArray(uint32_t* dst, size_t nwords); - - // outputs rwords() words. - void print(const char* tag); - - // outputs rwords() words. - void toArray(uint32_t* dst); - - int writeToGnubby(); - - 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 deleted file mode 100644 index 2012285f82..0000000000 --- a/util/signer/common/signed_header.h +++ /dev/null @@ -1,78 +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_COMMON_SIGNED_HEADER_H -#define __EC_UTIL_SIGNER_COMMON_SIGNED_HEADER_H - -#include -#include -#include - -#define FUSE_PADDING 0x55555555 // baked in hw! -#define FUSE_IGNORE 0xa3badaac // baked in rom! -#define FUSE_MAX 128 // baked in rom! - -#define INFO_MAX 128 // baked in rom! -#define INFO_IGNORE 0xaa3c55c3 // baked in rom! - -typedef struct SignedHeader { -#ifdef __cplusplus - SignedHeader() : magic(-1), image_size(0), - epoch_(0x1337), major_(0), minor_(0xbabe), - p4cl_(0), applysec_(0), config1_(0), err_response_(0), expect_response_(0) { - memset(signature, 'S', sizeof(signature)); - memset(tag, 'T', sizeof(tag)); - memset(fusemap, 0, sizeof(fusemap)); - memset(infomap, 0, sizeof(infomap)); - memset(_pad, '3', sizeof(_pad)); - } - - void markFuse(uint32_t n) { - assert(n < FUSE_MAX); - fusemap[n / 32] |= 1 << (n & 31); - } - - void markInfo(uint32_t n) { - assert(n < INFO_MAX); - infomap[n / 32] |= 1 << (n & 31); - } - - void print() const { - } -#endif // __cplusplus - - uint32_t magic; // -1 (thanks, boot_sys!) - uint32_t signature[96]; - uint32_t img_chk_; // top 32 bit of expected img_hash - // --------------------- everything below is part of img_hash - uint32_t tag[7]; // words 0-6 of RWR/FWR - uint32_t keyid; // word 7 of RWR - uint32_t key[96]; // public key to verify signature with - uint32_t image_size; - uint32_t ro_base; // readonly region - uint32_t ro_max; - uint32_t rx_base; // executable region - uint32_t rx_max; - uint32_t fusemap[FUSE_MAX / (8 * sizeof(uint32_t))]; - uint32_t infomap[INFO_MAX / (8 * sizeof(uint32_t))]; - uint32_t epoch_; // word 7 of FWR - uint32_t major_; // keyladder count - uint32_t minor_; - uint64_t timestamp_; // time of signing - uint32_t p4cl_; - uint32_t applysec_; // bits to and with FUSE_FW_DEFINED_BROM_APPLYSEC - uint32_t config1_; // bits to mesh with FUSE_FW_DEFINED_BROM_CONFIG1 - uint32_t err_response_; // bits to or with FUSE_FW_DEFINED_BROM_ERR_RESPONSE - uint32_t expect_response_; // action to take when expectation is violated - uint32_t _pad[256 - 1 - 96 - 1 - 7 - 1 - 96 - 5*1 - 4 - 4 - 9*1 - 2 - 1]; - uint32_t fuses_chk_; // top 32 bit of expected fuses hash - uint32_t info_chk_; // top 32 bit of expected info hash -} SignedHeader; - -#ifdef __cplusplus -static_assert(sizeof(SignedHeader) == 1024, "SignedHeader should be 1024 bytes"); -static_assert(offsetof(SignedHeader, info_chk_) == 1020, "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 deleted file mode 100644 index d5f9cfe25e..0000000000 --- a/util/signer/ecdh.cc +++ /dev/null @@ -1,47 +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. - */ -#include - -#include -#include - -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(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(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(out), &sha); - - EC_POINT_free(x); - EC_POINT_free(b); -} diff --git a/util/signer/gnubby.cc b/util/signer/gnubby.cc deleted file mode 100644 index 77cd5eb2a3..0000000000 --- a/util/signer/gnubby.cc +++ /dev/null @@ -1,634 +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. - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define MAX_APDU_SIZE 1200 -#define LIBUSB_ERR -1 - -extern bool FLAGS_verbose; - -// 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(...) do{if(FLAGS_verbose){fprintf(stderr, __VA_ARGS__);}}while(0) - -// 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() - : handle_(NULL) { - libusb_init(&ctx_); - libusb_set_debug(ctx_, 3); -} - -// 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) - home = 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()); -} - -static -bool isGnubby(libusb_device *dev) { - struct libusb_device_descriptor lsb_desc; - if (!libusb_get_device_descriptor(dev, &lsb_desc)) { - return lsb_desc.idVendor == 0x1050 && lsb_desc.idProduct == 0x0211; - } - return false; -} - -int Gnubby::doSign(EVP_MD_CTX* ctx, uint8_t* padded_req, uint8_t* signature, - uint32_t* siglen, EVP_PKEY* key) { - 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; - - // 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); - - uint8_t pubkey[256]; - if (key->type != EVP_PKEY_RSA - || EVP_PKEY_size(key) != 256 - || BN_bn2bin(key->pkey.rsa->n, pubkey) != 256) { - goto __fail; - } - if (memcmp(pubkey, resp + 1, 256)) { - // Key mis-match, wrong gnubby selected? - DLOG("pubkey mis-match, at device handle: %p", handle_); - goto __fail; - } - - 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; -} - -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; - DWORD image_hash_len = SHA256_DIGEST_LENGTH; - libusb_device **device_list; - ssize_t num_devices = libusb_get_device_list(ctx_, &device_list); - - // 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); - - for (int i = 0; i < num_devices; i++) { - if (!isGnubby(device_list[i])) { - continue; - } - int rc = libusb_open(device_list[i], &handle_); - if (rc) { - DLOG("libusb_open() @ device index: %d failed: %d\n", i, rc); - continue; - } - rc = libusb_claim_interface(handle_, 0); - if (rc) { - DLOG("libusb_claim_interface() @ device index: %d failed: %d\n", i, rc); - if (handle_) { - libusb_close(handle_); - handle_ = NULL; - } - continue; - } - rc = doSign(ctx, padded_req, signature, siglen, key); - libusb_release_interface(handle_, 0); - libusb_close(handle_); - handle_ = NULL; - if (rc == 1) { - result = 1; - break; - } - // Try next device. - } - - libusb_free_device_list(device_list, 1); - return result; -} - -// Open a gnubby, unspecified selection made when multiple plugged in. -int Gnubby::open() { - RESPONSECODE result = -1; - 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_); - } else { - result = 1; - } - return result; -} - -int Gnubby::write_bn(uint8_t p1, BIGNUM* n, size_t length) { - RESPONSECODE result = -1; - - UCHAR req[1024]; - UCHAR resp[1024]; - DWORD resp_len = 0; - - memcpy(req, "\x00\x66\x00\x00\x00\x00\x00", 7); - req[2] = p1; - req[5] = length >> 8; - req[6] = length; - - if (BN_bn2bin(n, req + 7) != int(length)) goto __fail; - - resp_len = sizeof(resp); - result = gnubby_apdu(handle_, - req, 7 + length, - resp, &resp_len); - if (result != 0) goto __fail; - - result = 1; - if (getSW12(resp, resp_len) != 0x9000) goto __fail; - - result = 0; - -__fail: - return result; -} - -int Gnubby::write(RSA* rsa) { - RESPONSECODE result = -1; - - UCHAR resp[2048]; - DWORD resp_len = 0; - - if (!handle_) { - result = (open() != 1); - if (result) goto __fail; - } - - // 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; - - result = 1; - if (getSW12(resp, resp_len) != 0x9000) goto __fail; - - result = 0; - - result = result || write_bn(0, rsa->p, 128); - result = result || write_bn(1, rsa->q, 128); - result = result || write_bn(2, rsa->dmp1, 128); - result = result || write_bn(3, rsa->dmq1, 128); - result = result || write_bn(4, rsa->iqmp, 128); - result = result || write_bn(5, rsa->n, 256); - result = result || write_bn(6, rsa->e, 1); - -__fail: - // (always try to) unlock - if (handle_) gnubby_lock(handle_, 0); - - return result; -} diff --git a/util/signer/image.cc b/util/signer/image.cc deleted file mode 100644 index a2e4d5c2fc..0000000000 --- a/util/signer/image.cc +++ /dev/null @@ -1,517 +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. - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -using namespace std; - -extern bool FLAGS_verbose; - -#define VERBOSE(...) \ - do { \ - if (FLAGS_verbose) fprintf(stderr, __VA_ARGS__); \ - } while (0) -#define WARN(...) \ - do { \ - fprintf(stderr, __VA_ARGS__); \ - } while (0) -#define FATAL(...) \ - do { \ - fprintf(stderr, __VA_ARGS__); \ - abort(); \ - } while (0) - -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 -} - -void Image::randomize() { - int fd = open("/dev/urandom", O_RDONLY); - if (fd >= 0) { - read(fd, mem_, sizeof(mem_)); - close(fd); - } -} - -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) { - WARN("failed to open '%s'\n", filename.c_str()); - goto fail; - } - if ((fstat(fd, &elf_stats)) < 0) { - WARN("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) { - WARN("cannot malloc %lu\n", elf_stats.st_size); - goto fail; - } - - if (read(fd, base_ptr, elf_stats.st_size) < elf_stats.st_size) { - WARN("cannot read from '%s'\n", filename.c_str()); - goto fail; - } - - // Sniff content for sanity - if (*(uint32_t*)base_ptr != 0x464c457f) { - // WARN("'%s' is not elf file\n", filename); - goto fail; - } - - if (elf_version(EV_CURRENT) == EV_NONE) { - WARN("Warning: elf library is out of date!\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); - - VERBOSE("type %08x; flags %08lx ", shdr.sh_type, shdr.sh_flags); - VERBOSE("%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) { - VERBOSE("phdr %08lx(@%08lx) [%08lx/%08lx]", phdr.p_vaddr, phdr.p_paddr, - phdr.p_filesz, phdr.p_memsz); - - // Ignore sections outside our flash range - if (phdr.p_paddr < FLASH_START * 16 || - phdr.p_paddr + phdr.p_filesz >= FLASH_END * 16) { - VERBOSE(" (outside flash, skipped)\n"); - continue; - } - - // Ignore p_offset 0, which ELF hdr; cannot be legit. - if (phdr.p_offset == 0) { - VERBOSE(" (offset 0, ignoring)\n"); - continue; - } - - VERBOSE("\n"); - - // Track ro boundaries - if (phdr.p_paddr < ro_base_) { - ro_base_ = phdr.p_paddr; - } - if (phdr.p_paddr + phdr.p_filesz > ro_max_) { - ro_max_ = phdr.p_paddr + phdr.p_filesz; - } - - // 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_; - - // Set ro_base to start, so app can read its own header. - ro_base_ = base_ + FLASH_START * 16; - // Set rx_base to just past header, where interrupt vectors are, - // since fetching a vector gets done on the I bus. - rx_base_ = ro_base_ + sizeof(SignedHeader); - - high_ = ((high_ + 2047) / 2048) * 2048; // Round image to multiple of 2K. - - VERBOSE("Rounded image size %lu\n", size()); - VERBOSE("ro_base %08lx..%08lx\n", ro_base_, ro_max_); - VERBOSE("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) { - WARN("short record %s", line); - success_ = false; - continue; - } - if (line[7] != '0') { - WARN("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; - // WARN("at segment %04x\n", seg); - } else { - WARN("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) { - WARN("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: { - WARN("unknown record type %s", line); - success_ = false; - } break; - } - } - fclose(fp); - } else { - WARN("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) { - WARN("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) { - WARN("unsigned images should start odd 1K boundary, not %08x\n", low_); - } - base_ = low_ - sizeof(SignedHeader); - } - } - - if (success_) { - VERBOSE("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(); - VERBOSE("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::fillPattern(uint32_t pattern) { - for (int i = high_ - base_; i < 512 * 1024 - 2048; i += 4) { - *(uint32_t*)(mem_ + i) = pattern; - } - high_ = 512 * 1024 - 2048; -} - -void Image::fillRandom() { - srand(time(NULL)); - for (int i = high_ - base_; i < 512 * 1024 - 2048; i += 4) { - *(uint32_t*)(mem_ + i) = rand(); - } - high_ = 512 * 1024 - 2048; -} - -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_ + base_, 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: - WARN("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_)) { - WARN("illegal adr %04x\n", adr); - success_ = false; - return; - } - // VERBOSE("mem_[0x%08x]=0x%02x\n", adr, v&255); - 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], const uint32_t info[INFO_MAX], - const string& hashesFilename) { - 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]; - uint8_t info_hash[SHA256_DIGEST_LENGTH]; - } hashes; - - memcpy(hdr, input_hdr, sizeof(SignedHeader)); - - hdr->image_size = this->size(); - - // Fill in key traits - hdr->keyid = key.n0inv(); - key.modToArray(hdr->key, key.rwords()); - - // Hash fuses - SHA256_Init(&sha256); - SHA256_Update(&sha256, fuses, FUSE_MAX * sizeof(uint32_t)); - SHA256_Final(hashes.fuses_hash, &sha256); - - hdr->fuses_chk_ = (hashes.fuses_hash[0] << 0) | (hashes.fuses_hash[1] << 8) | - (hashes.fuses_hash[2] << 16) | (hashes.fuses_hash[3] << 24); - - // Hash info - SHA256_Init(&sha256); - SHA256_Update(&sha256, info, INFO_MAX * sizeof(uint32_t)); - SHA256_Final(hashes.info_hash, &sha256); - - hdr->info_chk_ = (hashes.info_hash[0] << 0) | (hashes.info_hash[1] << 8) | - (hashes.info_hash[2] << 16) | (hashes.info_hash[3] << 24); - - // Hash img - int size = this->size() - offsetof(SignedHeader, tag); - SHA256_Init(&sha256); - SHA256_Update(&sha256, &hdr->tag, size); - SHA256_Final(hashes.img_hash, &sha256); - - hdr->img_chk_ = (hashes.img_hash[0] << 0) | (hashes.img_hash[1] << 8) | - (hashes.img_hash[2] << 16) | (hashes.img_hash[3] << 24); - - // Dump out values for comparing against boot_rom - VERBOSE("Himg ="); - for (size_t i = 0; i < sizeof(hashes.img_hash); ++i) { - VERBOSE("%02x", hashes.img_hash[i]); - } - VERBOSE("\n"); - - VERBOSE("Hfss ="); - for (size_t i = 0; i < sizeof(hashes.fuses_hash); ++i) { - VERBOSE("%02x", hashes.fuses_hash[i]); - } - VERBOSE("\n"); - - VERBOSE("Hinf ="); - for (size_t i = 0; i < sizeof(hashes.info_hash); ++i) { - VERBOSE("%02x", hashes.info_hash[i]); - } - VERBOSE("\n"); - - if (!hashesFilename.empty()) { - // Write hashes to file for subsequent extraneous (re)signing. - int fd = open(hashesFilename.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0600); - if (fd >= 0) { - write(fd, &hashes, sizeof(hashes)); - close(fd); - } - } - - sig = BN_bin2bn((uint8_t*)(hdr->signature), sizeof(hdr->signature), NULL); - - result = key.sign(&hashes, sizeof(hashes), &sig); - - if (result != 1) { - WARN("key.sign: %d\n", result); - } else { - key.toArray(hdr->signature, key.rwords(), sig); - } - - if (sig) BN_free(sig); - - return result == 1; -} diff --git a/util/signer/publickey.cc b/util/signer/publickey.cc deleted file mode 100644 index 4b977d123e..0000000000 --- a/util/signer/publickey.cc +++ /dev/null @@ -1,278 +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. - */ -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include - -extern bool FLAGS_verbose; - -#define VERBOSE(...) do{if(FLAGS_verbose){fprintf(stderr, __VA_ARGS__);fflush(stderr);}}while(0) -#define WARN(...) do{fprintf(stderr, __VA_ARGS__);}while(0) -#define FATAL(...) do{fprintf(stderr, __VA_ARGS__);abort();}while(0) - -PublicKey::PublicKey(const std::string& filename) : key_(NULL), publicOnly_(true) { - EVP_PKEY* pkey = NULL; - BIO* bio = BIO_new(BIO_s_file()); - - 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) { - VERBOSE("read public key only, assuming gnubby for signing..\n"); - } - } - } - - if (!pkey) { - WARN("loadKey: failed to load RSA key from '%s'\n", filename.c_str()); - } - - 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; -} - -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 + 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); - 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); -} - -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, rwords(), rsa->n); - 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); -} - -void PublicKey::modToArray(uint32_t* dst, size_t nwords) { - RSA* rsa = EVP_PKEY_get1_RSA(key_); - toArray(dst, nwords, rsa->n); - RSA_free(rsa); -} - -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); - - if (publicOnly_) { - if (nwords() == 64) { - // 2048 bit public key : gnubby - fprintf(stderr, "gnubby signing.."); fflush(stderr); - - Gnubby gnubby; - result = gnubby.sign(ctx, sig, &siglen, key_); - fprintf(stderr, "Gnubby.sign: %d\n", result); - } else { - // other public key : best have signature prefilled - fprintf(stderr, "WARNING: public key size %lu; assuming preloaded signature\n", nwords()); - fprintf(stderr, " Likely you are trying to use the real rom key, try the -dev flavor\n"); - fflush(stderr); - siglen = BN_bn2bin(*output, sig); - result = 1; - } - } else { - VERBOSE("ossl signing.."); - result = EVP_SignFinal(ctx, sig, &siglen, key_); - VERBOSE("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, rwords() * 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; -} - -int PublicKey::writeToGnubby() { - if (publicOnly_) return -1; - - RSA* rsa = EVP_PKEY_get1_RSA(key_); - - Gnubby gnubby; - int result = gnubby.write(rsa); - - RSA_free(rsa); - - return result; -}