mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
cr50: signer: sync up with upstream
This change just copies files shared between two repositories which have changed since the last sync up. This time it is as of @CL85098. BRANCH=none BUG=none TEST=the signed image boots fine on the b1 board. Change-Id: I7a1d1b344119e6f6729a38bbea04da75f2d3371c Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/329407 Reviewed-by: Marius Schilder <mschilder@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
committed by
chrome-bot
parent
b47c1fed20
commit
d1bf3aecfa
@@ -7,8 +7,8 @@
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
#include <rapidjson/document.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
@@ -32,17 +32,24 @@
|
||||
|
||||
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)
|
||||
#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) {
|
||||
static xmlChar* get_val(xmlNodePtr node, const char* key) {
|
||||
xmlNode* cur_node = NULL;
|
||||
xmlChar* val = NULL;
|
||||
|
||||
@@ -75,9 +82,8 @@ xmlChar* get_val(xmlNodePtr node, const char* key) {
|
||||
return val;
|
||||
}
|
||||
|
||||
static
|
||||
bool get_fuse(xmlNodePtr a_node,
|
||||
map<string, uint32_t>* ids, map<string, uint32_t>* bits) {
|
||||
static bool get_fuse(xmlNodePtr a_node, map<string, uint32_t>* ids,
|
||||
map<string, uint32_t>* bits) {
|
||||
bool result = false;
|
||||
|
||||
// Interested in <HashType>
|
||||
@@ -110,9 +116,8 @@ bool get_fuse(xmlNodePtr a_node,
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
bool find_fuses(xmlNodePtr a_node,
|
||||
map<string, uint32_t>* ids, map<string, uint32_t>* bits) {
|
||||
static bool find_fuses(xmlNodePtr a_node, map<string, uint32_t>* ids,
|
||||
map<string, uint32_t>* bits) {
|
||||
xmlNode* cur_node = NULL;
|
||||
bool done = false;
|
||||
|
||||
@@ -137,9 +142,8 @@ bool find_fuses(xmlNodePtr a_node,
|
||||
return done;
|
||||
}
|
||||
|
||||
static
|
||||
bool find_default_reg_value(xmlNodePtr a_node,
|
||||
const string& regname, string* result) {
|
||||
static bool find_default_reg_value(xmlNodePtr a_node, const string& regname,
|
||||
string* result) {
|
||||
xmlNode* cur_node = NULL;
|
||||
bool done = false;
|
||||
|
||||
@@ -168,12 +172,9 @@ bool find_default_reg_value(xmlNodePtr a_node,
|
||||
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 readXML(const string& filename, map<string, uint32_t>* ids,
|
||||
map<string, uint32_t>* bits, uint32_t* p4cl) {
|
||||
bool result = false;
|
||||
LIBXML_TEST_VERSION
|
||||
|
||||
@@ -182,7 +183,8 @@ bool readXML(const string& filename,
|
||||
if (doc) {
|
||||
result = find_fuses(xmlDocGetRootElement(doc), ids, bits);
|
||||
string p4clStr;
|
||||
result &= find_default_reg_value(xmlDocGetRootElement(doc), "SWDP_P4_LAST_SYNC", &p4clStr);
|
||||
result &= find_default_reg_value(xmlDocGetRootElement(doc),
|
||||
"SWDP_P4_LAST_SYNC", &p4clStr);
|
||||
if (result) {
|
||||
*p4cl = atoi(p4clStr.c_str());
|
||||
}
|
||||
@@ -195,84 +197,14 @@ bool readXML(const string& filename,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Write minified XML
|
||||
bool writeXML(const string& filename,
|
||||
map<string, uint32_t>& ids,
|
||||
map<string, uint32_t>& bits,
|
||||
uint32_t p4cl) {
|
||||
bool result = false;
|
||||
|
||||
// Get the names in order.
|
||||
vector<string> names;
|
||||
names.resize(ids.size());
|
||||
for (map<string, uint32_t>::const_iterator it = ids.begin(); it != ids.end(); ++it) {
|
||||
const string& name = it->first;
|
||||
uint32_t offset = it->second;
|
||||
names[offset] = name;
|
||||
}
|
||||
|
||||
// Write out as nodes.
|
||||
ofstream ofs(filename.c_str(), ofstream::out);
|
||||
if (ofs) {
|
||||
ofs << "<ArrayType>" << endl;
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
const string& name = names[i];
|
||||
ofs << "<ArrayItem>" << endl;
|
||||
ofs << " <HashType>" << endl;
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>RegName</Key>" << endl;
|
||||
ofs << " <Value>" << name << "</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>FuseLogicalOffset</Key>" << endl;
|
||||
ofs << " <Value>" << i << "</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>Width</Key>" << endl;
|
||||
ofs << " <Value>" << bits[name] << "</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
ofs << " </HashType>" << endl;
|
||||
ofs << "</ArrayItem>" << endl;
|
||||
}
|
||||
|
||||
// Write p4cl
|
||||
ofs << "<ArrayItem>" << endl;
|
||||
ofs << "<HashType>" << endl;
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>RegName</Key>" << endl;
|
||||
ofs << " <Value>SWDP_P4_LAST_SYNC</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>Default</Key>" << endl;
|
||||
ofs << " <Value>" << p4cl << "</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
// Write fake offset to make parser happy
|
||||
ofs << " <HashItem>" << endl;
|
||||
ofs << " <Key>FuseLogicalOffset</Key>" << endl;
|
||||
ofs << " <Value>0</Value>" << endl;
|
||||
ofs << " </HashItem>" << endl;
|
||||
ofs << "</HashType>" << endl;
|
||||
ofs << "</ArrayItem>" << endl;
|
||||
|
||||
ofs << "</ArrayType>" << endl;
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read JSON, populate map, name -> val
|
||||
bool readJSON(const string& filename,
|
||||
string* tag,
|
||||
map<string, uint32_t>* values,
|
||||
map<string, uint32_t>* fusemap,
|
||||
bool readJSON(const string& filename, string* tag,
|
||||
map<string, uint32_t>* values, map<string, uint32_t>* fusemap,
|
||||
map<string, uint32_t>* 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..
|
||||
@@ -290,27 +222,35 @@ bool readJSON(const string& filename,
|
||||
// Try parse.
|
||||
rapidjson::Document d;
|
||||
if (d.Parse(s.c_str()).HasParseError()) {
|
||||
FATAL("JSON %s[%lu]: parse error\n", filename.c_str(), d.GetErrorOffset());
|
||||
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);}; \
|
||||
#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(); \
|
||||
#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) {
|
||||
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) {
|
||||
for (rapidjson::Value::ConstMemberIterator it = infos.MemberBegin();
|
||||
it != infos.MemberEnd(); ++it) {
|
||||
(*infomap)[it->name.GetString()] = it->value.GetInt();
|
||||
}
|
||||
|
||||
@@ -333,7 +273,6 @@ bool readJSON(const string& filename,
|
||||
|
||||
#undef GETVALUE
|
||||
#undef CHECKVALUE
|
||||
|
||||
}
|
||||
}
|
||||
#endif // HAVE_JSON
|
||||
@@ -351,53 +290,57 @@ string hashesFilename;
|
||||
bool fillPattern = false;
|
||||
uint32_t pattern = -1;
|
||||
bool fillRandom = false;
|
||||
string xmlOutputFilename;
|
||||
|
||||
void usage(int argc, char* argv[]) {
|
||||
fprintf(stderr, "Usage: %s options\n"
|
||||
fprintf(stderr,
|
||||
"Usage: %s options\n"
|
||||
"--input=$elf-filename\n"
|
||||
"--output=output-filename\n"
|
||||
"--key=$pem-filename\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"
|
||||
"[--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"
|
||||
"[--writefuses=$xml-filename] to write out shortened xml\n"
|
||||
"[--verbose]\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'},
|
||||
{"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}
|
||||
};
|
||||
// name, has_arg
|
||||
{"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:w:hvr",
|
||||
long_options, &option_index)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "i:o:p:k:x:j:f:s:H:chvr", 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 'c':
|
||||
FLAGS_cros = true;
|
||||
break;
|
||||
case 'i':
|
||||
inputFilename.assign(optarg);
|
||||
break;
|
||||
@@ -410,9 +353,6 @@ int getOptions(int argc, char* argv[]) {
|
||||
case 'x':
|
||||
xmlFilename.assign(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
xmlOutputFilename.assign(optarg);
|
||||
break;
|
||||
case 's':
|
||||
signatureFilename.assign(optarg);
|
||||
break;
|
||||
@@ -444,9 +384,7 @@ int getOptions(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (inputFilename.empty() ||
|
||||
outputFilename.empty() ||
|
||||
keyFilename.empty() ||
|
||||
if (inputFilename.empty() || outputFilename.empty() || keyFilename.empty() ||
|
||||
((outputFormat != "bin") && (outputFormat != "hex"))) {
|
||||
usage(argc, argv);
|
||||
return 1;
|
||||
@@ -476,8 +414,10 @@ int main(int argc, char* argv[]) {
|
||||
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 I prefetch sets off GLOBALSEC when too tight
|
||||
// make sure these are nops or such?
|
||||
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.
|
||||
@@ -501,7 +441,8 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Fill in more of hdr, per manifest values
|
||||
for (map<string, uint32_t>::const_iterator it = values.begin(); it != values.end(); ++it) {
|
||||
for (map<string, uint32_t>::const_iterator it = values.begin();
|
||||
it != values.end(); ++it) {
|
||||
VERBOSE("%s : %u\n", it->first.c_str(), it->second);
|
||||
}
|
||||
|
||||
@@ -522,13 +463,21 @@ int main(int argc, char* argv[]) {
|
||||
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<string, uint32_t>::const_iterator it = fuses.begin(); it != fuses.end(); ++it) {
|
||||
for (map<string, uint32_t>::const_iterator it = fuses.begin();
|
||||
it != fuses.end(); ++it) {
|
||||
VERBOSE("fuse '%s' should have value %u\n", it->first.c_str(), it->second);
|
||||
}
|
||||
|
||||
@@ -543,8 +492,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
if (values["p4cl"] != xml_p4cl) {
|
||||
FATAL("mismatching p4cl: xml %u vs. json %u\n",
|
||||
xml_p4cl, values["p4cl"]);
|
||||
FATAL("mismatching p4cl: xml %u vs. json %u\n", xml_p4cl, values["p4cl"]);
|
||||
}
|
||||
|
||||
VERBOSE("found %lu fuse definitions\n", fuse_ids.size());
|
||||
@@ -560,24 +508,18 @@ int main(int argc, char* argv[]) {
|
||||
fuse_ids["FW_DEFINED_DATA_EXTRA_BLK6"] = 125;
|
||||
fuse_bits["FW_DEFINED_DATA_EXTRA_BLK6"] = 5;
|
||||
|
||||
for (map<string, uint32_t>::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]);
|
||||
}
|
||||
|
||||
// Write condensed XML if asked to.
|
||||
if (!xmlOutputFilename.empty()) {
|
||||
writeXML(xmlOutputFilename,
|
||||
fuse_ids,
|
||||
fuse_bits,
|
||||
xml_p4cl);
|
||||
for (map<string, uint32_t>::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<string, uint32_t>::const_iterator x = fuses.begin(); x != fuses.end(); ++x) {
|
||||
for (map<string, uint32_t>::const_iterator x = fuses.begin();
|
||||
x != fuses.end(); ++x) {
|
||||
map<string, uint32_t>::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());
|
||||
@@ -596,20 +538,18 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Print out fuse hash input.
|
||||
VERBOSE("expected fuse state:");
|
||||
VERBOSE("expected fuse state:\n");
|
||||
for (size_t i = 0; i < FUSE_MAX; ++i) {
|
||||
if (! (i % 8))
|
||||
VERBOSE("\n");
|
||||
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<string, uint32_t>::const_iterator x = infos.begin(); x != infos.end(); ++x) {
|
||||
for (map<string, uint32_t>::const_iterator x = infos.begin();
|
||||
x != infos.end(); ++x) {
|
||||
uint32_t index = atoi(x->first.c_str());
|
||||
assert(index < INFO_MAX);
|
||||
|
||||
@@ -621,10 +561,8 @@ int main(int argc, char* argv[]) {
|
||||
// TODO: read values from JSON or implement version logic here.
|
||||
|
||||
// Print out info hash input.
|
||||
VERBOSE("expected info state:");
|
||||
VERBOSE("expected info state:\n");
|
||||
for (size_t i = 0; i < INFO_MAX; ++i) {
|
||||
if (! (i % 8))
|
||||
VERBOSE("\n");
|
||||
VERBOSE("%08x ", info_values[i]);
|
||||
}
|
||||
VERBOSE("\n");
|
||||
@@ -635,7 +573,8 @@ int main(int argc, char* argv[]) {
|
||||
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());
|
||||
if (n != sizeof(hdr.signature))
|
||||
FATAL("cannot read from '%s'\n", signatureFilename.c_str());
|
||||
|
||||
VERBOSE("provided signature\n");
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#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 // baked in hw!
|
||||
#define FUSE_IGNORE 0xa3badaac // baked in rom!
|
||||
#define FUSE_MAX 128 // baked in rom!
|
||||
@@ -33,6 +37,9 @@ typedef struct SignedHeader {
|
||||
assert(n < INFO_MAX);
|
||||
infomap[n / 32] |= 1 << (n & 31);
|
||||
}
|
||||
|
||||
void print() const {
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
uint32_t magic; // -1 (thanks, boot_sys!)
|
||||
|
||||
@@ -294,8 +294,7 @@ void getPIN(uint8_t* out) {
|
||||
static
|
||||
std::string tokenFilename(const uint8_t* fp) {
|
||||
const char* home = getenv("HOME");
|
||||
if (home == NULL)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
if (home == NULL) getpwuid(getuid())->pw_dir;
|
||||
std::string s(home);
|
||||
s.append("/.tmp/");
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
@@ -568,10 +567,6 @@ int Gnubby::write_bn(uint8_t p1, BIGNUM* n, size_t length) {
|
||||
UCHAR resp[1024];
|
||||
DWORD resp_len = 0;
|
||||
|
||||
if (!handle_) {
|
||||
open();
|
||||
}
|
||||
|
||||
memcpy(req, "\x00\x66\x00\x00\x00\x00\x00", 7);
|
||||
req[2] = p1;
|
||||
req[5] = length >> 8;
|
||||
@@ -600,6 +595,11 @@ int Gnubby::write(RSA* rsa) {
|
||||
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;
|
||||
@@ -627,7 +627,7 @@ int Gnubby::write(RSA* rsa) {
|
||||
|
||||
__fail:
|
||||
// (always try to) unlock
|
||||
gnubby_lock(handle_, 0);
|
||||
if (handle_) gnubby_lock(handle_, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2,20 +2,9 @@
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include <common/image.h>
|
||||
|
||||
// global C++ includes
|
||||
#include <string>
|
||||
|
||||
// global C includes
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <libelf.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -24,27 +13,51 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// local includes
|
||||
#include <gelf.h>
|
||||
#include <libelf.h>
|
||||
|
||||
#include <common/publickey.h>
|
||||
#include <common/image.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include <common/signed_header.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
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)
|
||||
#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 = 0x40000;
|
||||
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), rx_base_(FLASH_END),
|
||||
ro_max_(0), rx_max_(0) {
|
||||
: 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
|
||||
}
|
||||
|
||||
@@ -78,9 +91,9 @@ bool Image::fromElf(const string& filename) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// printf("Elf filesize: %lu\n", elf_stats.st_size);
|
||||
// printf("Elf filesize: %lu\n", elf_stats.st_size);
|
||||
|
||||
if ((base_ptr = (char*) malloc(elf_stats.st_size)) == NULL) {
|
||||
if ((base_ptr = (char*)malloc(elf_stats.st_size)) == NULL) {
|
||||
WARN("cannot malloc %lu\n", elf_stats.st_size);
|
||||
goto fail;
|
||||
}
|
||||
@@ -91,8 +104,8 @@ bool Image::fromElf(const string& filename) {
|
||||
}
|
||||
|
||||
// Sniff content for sanity
|
||||
if (*(uint32_t*) base_ptr != 0x464c457f) {
|
||||
// WARN("'%s' is not elf file\n", filename);
|
||||
if (*(uint32_t*)base_ptr != 0x464c457f) {
|
||||
// WARN("'%s' is not elf file\n", filename);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -107,53 +120,49 @@ bool Image::fromElf(const string& filename) {
|
||||
gelf_getshdr(scn, &shdr);
|
||||
|
||||
VERBOSE("type %08x; flags %08lx ", shdr.sh_type, shdr.sh_flags);
|
||||
VERBOSE("%08lx(@%08lx)[%08lx] align %lu ",
|
||||
shdr.sh_addr, shdr.sh_offset, shdr.sh_size, shdr.sh_addralign);
|
||||
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)) {
|
||||
VERBOSE("non aloc, ignored\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore sections that are not exec
|
||||
if (!(shdr.sh_flags & SHF_EXECINSTR)) {
|
||||
VERBOSE("non exec, ignored\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore sections outside our flash range
|
||||
if (shdr.sh_addr < FLASH_START ||
|
||||
shdr.sh_addr + shdr.sh_size >= FLASH_END) {
|
||||
VERBOSE("out of bounds, ignored\n");
|
||||
if (shdr.sh_addr < FLASH_START * 16 ||
|
||||
shdr.sh_addr + shdr.sh_size >= FLASH_END * 16) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VERBOSE("\n");
|
||||
|
||||
// Track rx boundaries
|
||||
if (shdr.sh_addr < rx_base_) {
|
||||
rx_base_ = shdr.sh_addr;
|
||||
}
|
||||
if (shdr.sh_addr + shdr.sh_size> rx_max_) {
|
||||
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);
|
||||
VERBOSE("phdr %08lx(@%08lx) [%08lx/%08lx]", phdr.p_vaddr, phdr.p_paddr,
|
||||
phdr.p_filesz, phdr.p_memsz);
|
||||
|
||||
if (phdr.p_filesz != phdr.p_memsz) {
|
||||
VERBOSE(" (size mismatch, not loading)\n");
|
||||
// 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 sections outside our flash range
|
||||
if (phdr.p_paddr < FLASH_START ||
|
||||
phdr.p_paddr + phdr.p_memsz >= FLASH_END) {
|
||||
VERBOSE(" (out of bounds, not loading)\n");
|
||||
// Ignore p_offset 0, which ELF hdr; cannot be legit.
|
||||
if (phdr.p_offset == 0) {
|
||||
VERBOSE(" (offset 0, ignoring)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -169,8 +178,7 @@ bool Image::fromElf(const string& filename) {
|
||||
|
||||
// Copy data into image
|
||||
for (size_t n = 0; n < phdr.p_filesz; ++n) {
|
||||
store(phdr.p_paddr + n - FLASH_START,
|
||||
base_ptr[phdr.p_offset + n]);
|
||||
store(phdr.p_paddr + n - FLASH_START * 16, base_ptr[phdr.p_offset + n]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +186,7 @@ bool Image::fromElf(const string& filename) {
|
||||
base_ = low_;
|
||||
|
||||
// Set ro_base to start, so app can read its own header.
|
||||
ro_base_ = base_ + FLASH_START;
|
||||
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);
|
||||
@@ -216,54 +224,53 @@ bool Image::fromIntelHex(const string& filename, bool withSignature) {
|
||||
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/16 && s <= FLASH_END/16) {
|
||||
seg = s - FLASH_START/16;
|
||||
//WARN("at segment %04x\n", seg);
|
||||
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 {
|
||||
WARN("data should fit in range %x-%x: %s\n",
|
||||
FLASH_START, FLASH_END, line);
|
||||
success_ = false;
|
||||
store((seg * 16) + adr++, parseByte(&p));
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
case '3': { // 03 entry point
|
||||
} break;
|
||||
default: {
|
||||
WARN("unknown record type %s", line);
|
||||
success_ = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
@@ -290,14 +297,14 @@ bool Image::fromIntelHex(const string& filename, bool withSignature) {
|
||||
}
|
||||
|
||||
if (success_) {
|
||||
VERBOSE("low %08x, high %08x\n",
|
||||
FLASH_START + low_, FLASH_START + high_);
|
||||
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 + base_;
|
||||
rx_base_ = FLASH_START + base_;
|
||||
ro_max_ = FLASH_START + base_ + size();
|
||||
rx_max_ = FLASH_START + base_ + size();
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -306,20 +313,20 @@ bool Image::fromIntelHex(const string& filename, bool withSignature) {
|
||||
|
||||
Image::~Image() {}
|
||||
|
||||
void Image::toIntelHex(FILE *fout) const {
|
||||
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/16 + (base_>>4) + ((i - base_)>>4);
|
||||
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);
|
||||
(~((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);
|
||||
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", mem_[i + n]);
|
||||
crc += mem_[i + n];
|
||||
}
|
||||
fprintf(fout, "%02X", (~(crc & 255) + 1) & 255);
|
||||
fprintf(fout, "\n");
|
||||
@@ -327,7 +334,7 @@ void Image::toIntelHex(FILE *fout) const {
|
||||
}
|
||||
|
||||
void Image::fillPattern(uint32_t pattern) {
|
||||
for (int i = high_ - base_; i < 512*1024 - 2048; i += 4) {
|
||||
for (int i = high_ - base_; i < 512 * 1024 - 2048; i += 4) {
|
||||
*(uint32_t*)(mem_ + i) = pattern;
|
||||
}
|
||||
high_ = 512 * 1024 - 2048;
|
||||
@@ -335,7 +342,7 @@ void Image::fillPattern(uint32_t pattern) {
|
||||
|
||||
void Image::fillRandom() {
|
||||
srand(time(NULL));
|
||||
for (int i = high_ - base_; i < 512*1024 - 2048; i += 4) {
|
||||
for (int i = high_ - base_; i < 512 * 1024 - 2048; i += 4) {
|
||||
*(uint32_t*)(mem_ + i) = rand();
|
||||
}
|
||||
high_ = 512 * 1024 - 2048;
|
||||
@@ -352,18 +359,37 @@ void Image::generate(const std::string& filename, bool hex_output) const {
|
||||
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':
|
||||
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;
|
||||
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;
|
||||
@@ -393,15 +419,14 @@ void Image::store(int adr, int v) {
|
||||
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],
|
||||
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_]);
|
||||
@@ -428,22 +453,16 @@ bool Image::sign(PublicKey& key,
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
@@ -451,11 +470,8 @@ bool Image::sign(PublicKey& key,
|
||||
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);
|
||||
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 =");
|
||||
@@ -478,7 +494,7 @@ bool Image::sign(PublicKey& key,
|
||||
|
||||
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);
|
||||
int fd = open(hashesFilename.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0600);
|
||||
if (fd >= 0) {
|
||||
write(fd, &hashes, sizeof(hashes));
|
||||
close(fd);
|
||||
|
||||
Reference in New Issue
Block a user