mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
cleanup: remove a couple of unused functions and files
scripts/sign_data.sh is just a wrapper to do this:
./signature_digest_utility $1 $3 \
| openssl rsautl -sign -pkcs -inkey $2
AFAICT, that script is only invoked by the SignatureFile()
function in host/lib/file_keys.c, which is not referenced by
anything. I think I can remove both of those things.
Also remove utility/gbb_utility.cc, which should have been done
long ago in commit 6f39615.
BUG=none
BRANCH=ToT
TEST=make runalltests
Also ran it on daisy_spring-paladin and link-tot-paladin.
Change-Id: I16de5022765806f11bf6144d7ffd8cc849578a68
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/216719
Reviewed-by: Mike Frysinger <vapier@chromium.org>
This commit is contained in:
committed by
chrome-internal-fetch
parent
a1d9fe6eec
commit
bc3f0b74f9
145
README
145
README
@@ -1,30 +1,53 @@
|
||||
This directory contains a reference implementation for Chrome OS
|
||||
verified boot in firmware.
|
||||
|
||||
----------
|
||||
Directory Structure
|
||||
----------
|
||||
Directory Structure
|
||||
----------
|
||||
|
||||
The source is organized into distinct modules -
|
||||
|
||||
firmware/ - Contains ONLY the code required by the BIOS to validate
|
||||
the secure boot components. There shouldn't be any code in here that
|
||||
signs or generates images. BIOS should require ONLY this directory to
|
||||
implement secure boot. Refer to firmware/README for futher details.
|
||||
firmware/
|
||||
|
||||
cgpt/ - Utility to read/write/modify GPT partitions. Much like the
|
||||
gpt tool, but with support for Chrome OS extensiosn.
|
||||
Contains ONLY the code required by the BIOS to validate the secure boot
|
||||
components. There shouldn't be any code in here that signs or generates
|
||||
images. BIOS should require ONLY this directory to implement secure boot.
|
||||
Refer to firmware/README for futher details.
|
||||
|
||||
host/ - Miscellaneous functions used by userland utilities.
|
||||
cgpt/
|
||||
|
||||
utility/ - Utilities for generating and verifying signed
|
||||
firmware and kernel images, as well as arbitrary blobs.
|
||||
Utility to read/write/modify GPT partitions. Similar to GNU parted or any
|
||||
other GPT tool, but this has support for Chrome OS extensions.
|
||||
|
||||
tests/ - User-land tests and benchmarks that test the reference
|
||||
implementation. Please have a look at these if you'd like to
|
||||
understand how to use the reference implementation.
|
||||
host/
|
||||
|
||||
Miscellaneous functions needed by userland utilities.
|
||||
|
||||
futility/
|
||||
|
||||
The "firmware utility" tool, used to create, sign, and validate Chrome OS
|
||||
images.
|
||||
|
||||
utility/
|
||||
|
||||
Random other utilities, not necesssarily related to verified boot as such.
|
||||
|
||||
tests/
|
||||
|
||||
User-land tests and benchmarks that test the reference implementation.
|
||||
Please have a look at these if you'd like to understand how to use the
|
||||
reference implementation.
|
||||
|
||||
build/
|
||||
|
||||
The output directory where the generated files will be placed, and where
|
||||
tests are run.
|
||||
|
||||
scripts/
|
||||
|
||||
Tools and scripts used to generate and use new signing keypairs. These are
|
||||
typically used only on a secure machine.
|
||||
|
||||
build/ - a directory where the generated files go to.
|
||||
|
||||
--------------------
|
||||
Building and testing
|
||||
@@ -37,67 +60,63 @@ there are host environment build problems due to missing .h files, try
|
||||
researching what packages the files belong to and install the missing packages
|
||||
before reporting a problem.
|
||||
|
||||
To build the software run
|
||||
|
||||
make
|
||||
The commands are the more-or-less expected ones:
|
||||
|
||||
in the top level directory. The build output is placed in the ./build
|
||||
directory.
|
||||
|
||||
To run the tests either invoke
|
||||
|
||||
RUNTESTS=1 make
|
||||
|
||||
in the top level directory or
|
||||
|
||||
cd tests
|
||||
BUILD=../build make runtests
|
||||
make
|
||||
make runtests
|
||||
make install [ DESTDIR=/usr/local/bin ]
|
||||
|
||||
|
||||
----------
|
||||
Some useful utilities:
|
||||
|
||||
----------
|
||||
Some useful utilities:
|
||||
----------
|
||||
|
||||
vbutil_key Convert a public key into .vbpubk format
|
||||
vbutil_keyblock Wrap a public key inside a signature and checksum
|
||||
vbutil_firmware Create a .vblock with signature info for a
|
||||
firmware image
|
||||
vbutil_kernel Pack a kernel image, bootloader, and config into
|
||||
a signed binary
|
||||
futility vbutil_key Convert a public key into .vbpubk format
|
||||
futility vbutil_keyblock Wrap a public key inside a signature and checksum
|
||||
futility vbutil_firmware Create a .vblock with signature info for a
|
||||
firmware image
|
||||
futility vbutil_kernel Pack a kernel image, bootloader, and config into
|
||||
a signed binary
|
||||
|
||||
dumpRSAPublicKey Dump RSA Public key (from a DER-encoded X509
|
||||
certificate) in a format suitable for use by
|
||||
RSAVerify* functions in crypto/.
|
||||
|
||||
verify_data.c Verify a given signature on a given file.
|
||||
|
||||
dumpRSAPublicKey Dump RSA Public key (from a DER-encoded X509
|
||||
certificate) in a format suitable for
|
||||
use by RSAVerify* functions in
|
||||
crypto/.
|
||||
|
||||
verify_data.c Verify a given signature on a given file.
|
||||
|
||||
|
||||
|
||||
----------
|
||||
Generating a signed firmware image:
|
||||
----------
|
||||
Generating a signed firmware image:
|
||||
----------
|
||||
|
||||
* Step 0: Build the tools, install them somewhere.
|
||||
|
||||
* Step 1: Generate RSA root and signing keys.
|
||||
|
||||
# Root key is always 8192 bits.
|
||||
$ openssl genrsa -F4 -out root_key.pem 8192
|
||||
The root key is always 8192 bits.
|
||||
|
||||
# Signing key can be between 1024-8192 bits.
|
||||
$ openssl genrsa -F4 -out signing_key.pem <1024|2048|4096|8192>
|
||||
$ openssl genrsa -F4 -out root_key.pem 8192
|
||||
|
||||
Note: The -F4 option must be specified to generate RSA keys with
|
||||
a public exponent of 65535. RSA keys with 3 as a public
|
||||
exponent (the default) won't work.
|
||||
The signing key can be between 1024-8192 bits.
|
||||
|
||||
$ openssl genrsa -F4 -out signing_key.pem <1024|2048|4096|8192>
|
||||
|
||||
Note: The -F4 option must be specified to generate RSA keys with a public
|
||||
exponent of 65535. RSA keys with 3 as a public exponent (the default)
|
||||
won't work.
|
||||
|
||||
* Step 2: Generate pre-processed public versions of the above keys using
|
||||
utility/dumpRSAPublicKey
|
||||
dumpRSAPublicKey. This utility expects an x509 certificate as
|
||||
input, and emits an intermediate representation for further
|
||||
processing.
|
||||
|
||||
# dumpRSAPublicKey expects an x509 certificate as input.
|
||||
$ openssl req -batch -new -x509 -key root_key.pem -out root_key.crt
|
||||
$ openssl req -batch -new -x509 -key signing_key.pem -out signing_key.crt
|
||||
$ utility/dumpRSAPublicKey root_key.crt > root_key.keyb
|
||||
$ utility/dumpRSAPublicKey signing_key.crt > signing_key.keyb
|
||||
$ openssl req -batch -new -x509 -key root_key.pem -out root_key.crt
|
||||
$ openssl req -batch -new -x509 -key signing_key.pem -out signing_key.crt
|
||||
$ dumpRSAPublicKey root_key.crt > root_key.keyb
|
||||
$ dumpRSAPublicKey signing_key.crt > signing_key.keyb
|
||||
|
||||
************** TODO: STUFF PAST HERE IS OUT OF DATE ***************
|
||||
|
||||
@@ -121,13 +140,13 @@ $ utility/firmware_utility --generate \
|
||||
--in <firmware blob file> \
|
||||
--out <output file>
|
||||
|
||||
Where <algoid> is based on the signature algorithm to use for firmware
|
||||
Where <algoid> is based on the signature algorithm to use for firmware
|
||||
signining. The list of <algoid> specifications can be output by running
|
||||
'utility/firmware_utility' without any arguments.
|
||||
|
||||
Note: --firmware_key_version and --firmware_version are part of a signed
|
||||
Note: --firmware_key_version and --firmware_version are part of a signed
|
||||
image and are used to prevent rollbacks to older version. For testing,
|
||||
they can just be set valid values.
|
||||
they can just be set to valid values.
|
||||
|
||||
|
||||
* Step 4: Verify that this image verifies.
|
||||
@@ -144,8 +163,8 @@ Note: The verification functions expects a pointer to the
|
||||
final firmware, this will be a fixed public key which cannot be
|
||||
changed and must be stored in RO firmware.
|
||||
|
||||
----------
|
||||
Generating a signed kernel image:
|
||||
----------
|
||||
Generating a signed kernel image:
|
||||
----------
|
||||
|
||||
The steps for generating a signed kernel image are similar to that of
|
||||
|
||||
@@ -79,42 +79,3 @@ uint8_t* DigestFile(char* input_file, int sig_algorithm) {
|
||||
close(input_fd);
|
||||
return digest;
|
||||
}
|
||||
|
||||
uint8_t* SignatureFile(const char* input_file, const char* key_file,
|
||||
unsigned int algorithm) {
|
||||
char* sign_utility = "./sign_data.sh";
|
||||
char* cmd; /* Command line to invoke. */
|
||||
int cmd_len;
|
||||
FILE* cmd_out; /* File descriptor to command output. */
|
||||
uint8_t* signature = NULL;
|
||||
int signature_size = siglen_map[algorithm];
|
||||
|
||||
/* Build command line:
|
||||
* sign_data.sh <algorithm> <key file> <input file>
|
||||
*/
|
||||
cmd_len = (strlen(sign_utility) + 1 + /* +1 for space. */
|
||||
2 + 1 + /* For [algorithm]. */
|
||||
strlen(key_file) + 1 + /* +1 for space. */
|
||||
strlen(input_file) +
|
||||
1); /* For the trailing '\0'. */
|
||||
cmd = (char*) malloc(cmd_len);
|
||||
snprintf(cmd, cmd_len, "%s %u %s %s", sign_utility, algorithm, key_file,
|
||||
input_file);
|
||||
cmd_out = popen(cmd, "r");
|
||||
free(cmd);
|
||||
if (!cmd_out) {
|
||||
VBDEBUG(("Couldn't execute: %s\n", cmd));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
signature = (uint8_t*) malloc(signature_size);
|
||||
if (fread(signature, signature_size, 1, cmd_out) != 1) {
|
||||
VBDEBUG(("Couldn't read signature.\n"));
|
||||
pclose(cmd_out);
|
||||
free(signature);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pclose(cmd_out);
|
||||
return signature;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,4 @@ RSAPublicKey* RSAPublicKeyFromFile(const char* input_file);
|
||||
*/
|
||||
uint8_t* DigestFile(char* input_file, int sig_algorithm);
|
||||
|
||||
/* Helper function to invoke external program to calculate signature on
|
||||
* [input_file] using private key [key_file] and signature algorithm
|
||||
* [algorithm].
|
||||
*
|
||||
* Returns the signature. Caller owns the buffer and must Free() it.
|
||||
*/
|
||||
uint8_t* SignatureFile(const char* input_file, const char* key_file,
|
||||
unsigned int algorithm);
|
||||
|
||||
#endif /* VBOOT_REFERENCE_FILE_KEYS_H_ */
|
||||
|
||||
@@ -47,7 +47,6 @@ int main(void)
|
||||
BufferFromFile(0, 0);
|
||||
RSAPublicKeyFromFile(0);
|
||||
DigestFile(0, 0);
|
||||
SignatureFile(0, 0, 0);
|
||||
|
||||
/* signature_digest.h */
|
||||
PrependDigestInfo(0, 0);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2010 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.
|
||||
|
||||
if [ $# -ne 3 ]
|
||||
then
|
||||
echo "Usage: `basename $0` <algorithm> <key file> <input file>"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
./signature_digest_utility $1 $3 | openssl rsautl -sign -pkcs -inkey $2
|
||||
@@ -1,879 +0,0 @@
|
||||
// Copyright 2011 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.
|
||||
//
|
||||
// Utility for manipulating Google Binary Block (GBB)
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "gbb_utility.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Simple File Utilities
|
||||
|
||||
// utility function: read a non-empty file.
|
||||
// return file content, or empty for any failure.
|
||||
static string read_nonempty_file(const char *filename) {
|
||||
string file_content;
|
||||
std::vector<char> buffer; // since image files are small, should be OK
|
||||
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
perror(filename);
|
||||
return file_content;
|
||||
}
|
||||
|
||||
// prepare buffer on successful seek
|
||||
if (fseek(fp, 0, SEEK_END) == 0) {
|
||||
buffer.resize(ftell(fp));
|
||||
rewind(fp);
|
||||
}
|
||||
|
||||
if (!buffer.empty()) {
|
||||
if (fread(&buffer[0], buffer.size(), 1, fp) != 1) {
|
||||
perror(filename);
|
||||
buffer.clear(); // discard buffer when read fail.
|
||||
} else {
|
||||
file_content.assign(buffer.begin(), buffer.end());
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return file_content;
|
||||
}
|
||||
|
||||
// utility function: write non-empty content to file.
|
||||
// return true on success, otherwise false.
|
||||
static bool write_nonempty_file(const char *filename, const string &content) {
|
||||
assert(!content.empty());
|
||||
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
perror(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
int r = fwrite(content.c_str(), content.size(), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (r != 1)
|
||||
perror(filename);
|
||||
|
||||
return r == 1;
|
||||
}
|
||||
|
||||
// utility function: convert integer to little-endian encoded bytes
|
||||
// return the byte array in string type
|
||||
static string int2bytes(const uint32_t value) {
|
||||
const char *pvalue = reinterpret_cast<const char*>(&value);
|
||||
return string(pvalue, sizeof(value));
|
||||
}
|
||||
|
||||
// utility function: convert little-endian encoded bytes to integer
|
||||
// return value in uint32_t type
|
||||
static uint32_t bytes2int(const string &bytes) {
|
||||
assert(bytes.size() == sizeof(uint32_t));
|
||||
return *reinterpret_cast<const uint32_t*>(bytes.c_str());
|
||||
}
|
||||
|
||||
// utility function: compare a GBB header with given version numbers.
|
||||
// return 1 for "larger", 0 for "equal" and -1 for "smaller".
|
||||
static int version_compare(const GoogleBinaryBlockHeader& header,
|
||||
int major, int minor) {
|
||||
if (header.major_version != major)
|
||||
return header.major_version - major;
|
||||
return header.minor_version - minor;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// GBB Utility implementation
|
||||
|
||||
namespace vboot_reference {
|
||||
|
||||
GoogleBinaryBlockUtil::GoogleBinaryBlockUtil() {
|
||||
assert(sizeof(header_) == GBB_HEADER_SIZE);
|
||||
initialize();
|
||||
}
|
||||
|
||||
GoogleBinaryBlockUtil::~GoogleBinaryBlockUtil() {
|
||||
}
|
||||
|
||||
void GoogleBinaryBlockUtil::initialize() {
|
||||
verbose = true;
|
||||
is_valid_gbb = false;
|
||||
header_offset_ = 0;
|
||||
memset(&header_, 0, sizeof(header_));
|
||||
file_content_.clear();
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::create_new(
|
||||
const std::vector<uint32_t> &create_param) {
|
||||
uint32_t *prop = &header_.hwid_offset; // must be first entry.
|
||||
uint32_t allocated_size = sizeof(header_);
|
||||
std::vector<uint32_t>::const_iterator i = create_param.begin();
|
||||
|
||||
// max properties = available space in header / size of record (offset+size)
|
||||
size_t max_properties =
|
||||
(sizeof(header_) - (reinterpret_cast<uint8_t*>(prop) -
|
||||
reinterpret_cast<uint8_t*>(&header_))) /
|
||||
(sizeof(uint32_t) * 2);
|
||||
|
||||
if (create_param.size() >= max_properties) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "error: creation parameters cannot exceed %zu entries.\n",
|
||||
max_properties);
|
||||
return false;
|
||||
}
|
||||
|
||||
initialize();
|
||||
memcpy(header_.signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE);
|
||||
header_.major_version = GBB_MAJOR_VER;
|
||||
header_.minor_version = GBB_MINOR_VER;
|
||||
header_.header_size = GBB_HEADER_SIZE;
|
||||
|
||||
while (i != create_param.end()) {
|
||||
*prop++ = allocated_size; // property offset
|
||||
*prop++ = *i; // property size
|
||||
allocated_size += *i;
|
||||
i++;
|
||||
}
|
||||
|
||||
file_content_.resize(allocated_size);
|
||||
std::copy(reinterpret_cast<char*>(&header_),
|
||||
reinterpret_cast<char*>(&header_ + 1),
|
||||
file_content_.begin());
|
||||
is_valid_gbb = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool GoogleBinaryBlockUtil::load_from_file(const char *filename) {
|
||||
is_valid_gbb = false;
|
||||
|
||||
file_content_ = read_nonempty_file(filename);
|
||||
if (file_content_.empty())
|
||||
return false;
|
||||
|
||||
switch (search_header_signatures(file_content_, &header_offset_)) {
|
||||
case 0:
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: cannot find any GBB signature.\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// fetch a copy of block header to check more detail
|
||||
if (!load_gbb_header(file_content_, header_offset_, &header_)) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: invalid GBB in image file.\n");
|
||||
} else {
|
||||
is_valid_gbb = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: found multiple GBB signatures.\n");
|
||||
file_content_.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// discard if anything goes wrong
|
||||
if (!is_valid_gbb)
|
||||
initialize();
|
||||
|
||||
return is_valid_gbb;
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::save_to_file(const char *filename) {
|
||||
assert(is_valid_gbb && !file_content_.empty());
|
||||
return write_nonempty_file(filename, file_content_);
|
||||
}
|
||||
|
||||
int GoogleBinaryBlockUtil::search_header_signatures(const string &image,
|
||||
long *poffset) const {
|
||||
int found_signatures = 0;
|
||||
size_t last_found_pos = 0;
|
||||
|
||||
while ((last_found_pos =
|
||||
file_content_.find(GBB_SIGNATURE, last_found_pos, GBB_SIGNATURE_SIZE))
|
||||
!= file_content_.npos) {
|
||||
*poffset = last_found_pos;
|
||||
found_signatures++;
|
||||
last_found_pos++; // for next iteration
|
||||
}
|
||||
|
||||
return found_signatures;
|
||||
}
|
||||
|
||||
// utility function for load_gbb_header to check property range
|
||||
static bool check_property_range(uint32_t off, uint32_t sz,
|
||||
uint32_t hdr_sz, uint32_t max_sz,
|
||||
const char *prop_name, bool verbose) {
|
||||
// for backward compatibility, we allow zero entry here.
|
||||
if (off == 0 && sz == 0) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " warning: property %s is EMPTY.\n", prop_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (off + sz > max_sz) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: property %s exceed GBB.\n", prop_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (off < hdr_sz) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: property %s overlap GBB header.\n", prop_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset,
|
||||
GoogleBinaryBlockHeader *phdr) const {
|
||||
assert(phdr);
|
||||
|
||||
// check that GBB header does not extend past end of image
|
||||
if (image.size() < (size_t)offset + GBB_HEADER_SIZE) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: incomplete GBB.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
string::const_iterator block_ptr = image.begin() + offset;
|
||||
size_t block_size = image.size() - offset;
|
||||
|
||||
std::copy(block_ptr, block_ptr + GBB_HEADER_SIZE,
|
||||
reinterpret_cast<char*>(phdr));
|
||||
|
||||
const GoogleBinaryBlockHeader &h = *phdr; // for quick access
|
||||
|
||||
// check version
|
||||
if (h.major_version != GBB_MAJOR_VER) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: invalid GBB version (%d.%d)\n",
|
||||
h.major_version, h.minor_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (h.header_size < GBB_HEADER_SIZE) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " error: incompatible header size (%d < %d)\n",
|
||||
h.header_size, GBB_HEADER_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify properties
|
||||
for (int i = 0; i < PROP_RANGE; i++) {
|
||||
uint32_t off, size;
|
||||
const char *name;
|
||||
|
||||
if (!find_property(static_cast<PROPINDEX>(i),
|
||||
&off, &size, &name)) {
|
||||
assert(!"invalid property.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_property_range(off, size,
|
||||
h.header_size, block_size, name, verbose))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::find_property(PROPINDEX i,
|
||||
uint32_t *poffset,
|
||||
uint32_t *psize,
|
||||
const char** pname) const {
|
||||
switch (i) {
|
||||
case PROP_FLAGS:
|
||||
*poffset = (uint8_t*)&header_.flags - (uint8_t*)&header_;
|
||||
*psize = sizeof(header_.flags);
|
||||
if (pname)
|
||||
*pname = "flags";
|
||||
break;
|
||||
|
||||
case PROP_HWID:
|
||||
*poffset = header_.hwid_offset;
|
||||
*psize = header_.hwid_size;
|
||||
if (pname)
|
||||
*pname = "hardware_id";
|
||||
break;
|
||||
|
||||
case PROP_ROOTKEY:
|
||||
*poffset = header_.rootkey_offset;
|
||||
*psize = header_.rootkey_size;
|
||||
if (pname)
|
||||
*pname = "root_key";
|
||||
break;
|
||||
|
||||
case PROP_BMPFV:
|
||||
*poffset = header_.bmpfv_offset;
|
||||
*psize = header_.bmpfv_size;
|
||||
if (pname)
|
||||
*pname = "bmp_fv";
|
||||
break;
|
||||
|
||||
case PROP_RCVKEY:
|
||||
*poffset = header_.recovery_key_offset;;
|
||||
*psize = header_.recovery_key_size;
|
||||
if (pname)
|
||||
*pname = "recovery_key";
|
||||
break;
|
||||
|
||||
default:
|
||||
if (verbose) {
|
||||
fprintf(stderr, " internal error: unknown property (%d).\n",
|
||||
static_cast<int>(i));
|
||||
}
|
||||
assert(!"invalid property index.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) {
|
||||
uint32_t prop_size;
|
||||
uint32_t prop_offset;
|
||||
const char *prop_name;
|
||||
|
||||
assert(is_valid_gbb);
|
||||
|
||||
if (!find_property(i, &prop_offset, &prop_size, &prop_name))
|
||||
return false;
|
||||
|
||||
// special processing by version
|
||||
if (version_compare(header_, 1, 1) < 0) {
|
||||
if (i == PROP_FLAGS) {
|
||||
assert(value.size() == prop.size());
|
||||
if (int2bytes(0) != value) {
|
||||
if (verbose)
|
||||
fprintf(stderr,
|
||||
"error: property %s is not supported on GBB version %d.%d\n",
|
||||
prop_name, header_.major_version, header_.minor_version);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (prop_size < value.size()) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "error: value size (%zu) exceed property capacity "
|
||||
"(%u): %s\n", value.size(), prop_size, prop_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// special properties
|
||||
switch (i) {
|
||||
case PROP_HWID:
|
||||
if (value.size() == prop_size) {
|
||||
if (verbose)
|
||||
fprintf(stderr,
|
||||
"error: NUL-terminated string exceed capacity (%d): %s\n",
|
||||
prop_size, prop_name);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_FLAGS:
|
||||
assert(value.size() == prop_size);
|
||||
header_.flags = bytes2int(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
string::iterator dest = file_content_.begin() + header_offset_ + prop_offset;
|
||||
file_content_.replace(dest, dest+prop_size, prop_size, '\0'); // wipe first
|
||||
std::copy(value.begin(), value.end(), dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string GoogleBinaryBlockUtil::get_property(PROPINDEX i) const {
|
||||
uint32_t prop_size;
|
||||
uint32_t prop_offset;
|
||||
const char *prop_name;
|
||||
|
||||
assert(is_valid_gbb);
|
||||
|
||||
if (!find_property(i, &prop_offset, &prop_size, &prop_name))
|
||||
return "";
|
||||
|
||||
// check range again to allow empty value (for compatbility)
|
||||
if (prop_offset == 0 && prop_size == 0) {
|
||||
if (verbose)
|
||||
fprintf(stderr, " warning: empty property (%d): %s.\n",
|
||||
static_cast<int>(i), prop_name);
|
||||
return "";
|
||||
}
|
||||
|
||||
// special processing by version
|
||||
if (version_compare(header_, 1, 1) < 0) {
|
||||
if (i == PROP_FLAGS)
|
||||
return int2bytes(0);
|
||||
}
|
||||
|
||||
string::const_iterator dest = file_content_.begin() +
|
||||
header_offset_ + prop_offset;
|
||||
return string(dest, dest + prop_size);
|
||||
}
|
||||
|
||||
string GoogleBinaryBlockUtil::get_property_name(PROPINDEX i) const {
|
||||
uint32_t unused_off, unused_size;
|
||||
const char *prop_name;
|
||||
|
||||
if (!find_property(i, &unused_off, &unused_size, &prop_name)) {
|
||||
assert(!"invalid property index.");
|
||||
return "";
|
||||
}
|
||||
|
||||
return prop_name;
|
||||
}
|
||||
|
||||
uint32_t GoogleBinaryBlockUtil::get_flags() const {
|
||||
return bytes2int(get_property(PROP_FLAGS));
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_flags(const uint32_t flags) {
|
||||
return set_property(PROP_FLAGS, int2bytes(flags));
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_hwid(const char *hwid) {
|
||||
return set_property(PROP_HWID, hwid);
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_rootkey(const std::string &value) {
|
||||
return set_property(PROP_ROOTKEY, value);
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_bmpfv(const string &value) {
|
||||
return set_property(PROP_BMPFV, value);
|
||||
}
|
||||
|
||||
bool GoogleBinaryBlockUtil::set_recovery_key(const string &value) {
|
||||
return set_property(PROP_RCVKEY, value);
|
||||
}
|
||||
|
||||
} // namespace vboot_reference
|
||||
|
||||
#ifndef FOR_LIBRARY
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// command line utilities
|
||||
|
||||
#include <map>
|
||||
|
||||
using vboot_reference::GoogleBinaryBlockUtil;
|
||||
|
||||
// utility function: provide usage of this utility and exit.
|
||||
static void usagehelp_exit(const char *prog_name) {
|
||||
const char *basename = strrchr(prog_name, '/');
|
||||
if (basename)
|
||||
basename++;
|
||||
else
|
||||
basename = prog_name;
|
||||
fprintf(stderr,
|
||||
"Utility to manage Google Binary Block (GBB)\n"
|
||||
"Usage: %s [-g|-s|-c] [OPTIONS] bios_file [output_file]\n"
|
||||
"\n"
|
||||
"GET MODE:\n"
|
||||
"-g, --get (default)\tGet (read) from bios_file, "
|
||||
"with following options:\n"
|
||||
" --hwid \tReport hardware id (default).\n"
|
||||
" --flags \tReport header flags.\n"
|
||||
" -k, --rootkey=FILE \tFile name to export Root Key.\n"
|
||||
" -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n"
|
||||
" --recoverykey=FILE\tFile name to export Recovery Key.\n"
|
||||
"\n"
|
||||
"SET MODE:\n"
|
||||
"-s, --set \tSet (write) to bios_file, "
|
||||
"with following options:\n"
|
||||
" -o, --output=FILE \tNew file name for ouptput.\n"
|
||||
" -i, --hwid=HWID \tThe new hardware id to be changed.\n"
|
||||
" --flags=FLAGS \tThe new (numeric) flags value.\n"
|
||||
" -k, --rootkey=FILE \tFile name of new Root Key.\n"
|
||||
" -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n"
|
||||
" --recoverykey=FILE\tFile name of new Recovery Key.\n"
|
||||
"\n"
|
||||
"CREATE MODE:\n"
|
||||
"-c, --create=prop1_size,prop2_size...\n"
|
||||
" \tCreate a GBB blob by given size list.\n"
|
||||
"SAMPLE:\n"
|
||||
" %s -g bios.bin\n"
|
||||
" %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n"
|
||||
" %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n",
|
||||
basename, basename, basename, basename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// utility function: export a property from GBB to given file.
|
||||
// if filename was empty, export to console (screen).
|
||||
// return true on success, otherwise false.
|
||||
static bool export_property(GoogleBinaryBlockUtil::PROPINDEX idx,
|
||||
const string &filename,
|
||||
const GoogleBinaryBlockUtil &util) {
|
||||
string prop_name = util.get_property_name(idx),
|
||||
value = util.get_property(idx);
|
||||
const char *name = prop_name.c_str();
|
||||
|
||||
if (filename.empty()) {
|
||||
// write to console
|
||||
if (idx == GoogleBinaryBlockUtil::PROP_FLAGS)
|
||||
printf("%s: 0x%08x\n", name, bytes2int(value));
|
||||
else
|
||||
printf("%s: %s\n", name, value.c_str());
|
||||
} else {
|
||||
const char *fn = filename.c_str();
|
||||
|
||||
if (!write_nonempty_file(fn, value)) {
|
||||
fprintf(stderr, "error: failed to export %s to file: %s\n", name, fn);
|
||||
return false;
|
||||
}
|
||||
printf(" - exported %s to file: %s\n", name, fn);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// utility function: import a property to GBB by given source (file or string).
|
||||
// return true on success, otherwise false.
|
||||
// is succesfully imported into GBB.
|
||||
static bool import_property(
|
||||
GoogleBinaryBlockUtil::PROPINDEX idx, const string &source,
|
||||
bool source_as_file, GoogleBinaryBlockUtil *putil) {
|
||||
assert(!source.empty());
|
||||
string prop_name = putil->get_property_name(idx);
|
||||
|
||||
if (source_as_file) {
|
||||
printf(" - import %s from %s: ", prop_name.c_str(), source.c_str());
|
||||
string v = read_nonempty_file(source.c_str());
|
||||
if (v.empty()) {
|
||||
printf("invalid file.\n");
|
||||
return false;
|
||||
}
|
||||
if (!putil->set_property(idx, v)) {
|
||||
printf("invalid content.\n");
|
||||
return false;
|
||||
}
|
||||
printf("success.\n");
|
||||
} else {
|
||||
// source as string
|
||||
string old_value = putil->get_property(idx);
|
||||
bool result = putil->set_property(idx, source);
|
||||
if (idx == GoogleBinaryBlockUtil::PROP_FLAGS)
|
||||
printf(" - %s changed from 0x%08x to 0x%08x: %s\n",
|
||||
prop_name.c_str(), bytes2int(old_value), bytes2int(source),
|
||||
result ? "success" : "failed");
|
||||
else
|
||||
printf(" - %s changed from '%s' to '%s': %s\n",
|
||||
prop_name.c_str(), old_value.c_str(), source.c_str(),
|
||||
result ? "success" : "failed");
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_creation_param(const string &input_string,
|
||||
std::vector<uint32_t> *output_vector) {
|
||||
const char *input = input_string.c_str();
|
||||
char *parsed = NULL;
|
||||
uint32_t param;
|
||||
|
||||
if (input_string.empty())
|
||||
return false;
|
||||
|
||||
do {
|
||||
param = (uint32_t)strtol(input, &parsed, 0);
|
||||
if (*parsed && *parsed != ',')
|
||||
return false;
|
||||
output_vector->push_back(param);
|
||||
input = *parsed ? parsed + 1 : parsed;
|
||||
} while (*input);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// main
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *myname = argv[0];
|
||||
int err_stage = 0; // an indicator for error exits
|
||||
GoogleBinaryBlockUtil util;
|
||||
|
||||
// small parameter helper class
|
||||
class OptPropertyMap: public
|
||||
std::map<GoogleBinaryBlockUtil::PROPINDEX, string> {
|
||||
public:
|
||||
bool set_new_value(GoogleBinaryBlockUtil::PROPINDEX id, const string &v) {
|
||||
if (find(id) != end())
|
||||
return false;
|
||||
|
||||
(*this)[id] = v;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
OptPropertyMap opt_props;
|
||||
|
||||
struct GBBUtilOptions {
|
||||
bool get_mode, set_mode, create_mode;
|
||||
string input_fn, output_fn;
|
||||
std::vector<uint32_t> create_param;
|
||||
} myopts;
|
||||
myopts.get_mode = myopts.set_mode = myopts.create_mode = false;
|
||||
|
||||
// snippets for getopt_long
|
||||
int option_index, opt;
|
||||
static struct option long_options[] = {
|
||||
{"get", 0, NULL, 'g' },
|
||||
{"set", 0, NULL, 's' },
|
||||
{"create", 1, NULL, 'c' },
|
||||
{"output", 1, NULL, 'o' },
|
||||
{"hwid", 2, NULL, 'i' },
|
||||
{"rootkey", 1, NULL, 'k' },
|
||||
{"bmpfv", 1, NULL, 'b' },
|
||||
{"recoverykey", 1, NULL, 'R' },
|
||||
{"flags", 2, NULL, 'L' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
// parse command line options
|
||||
while ((opt = getopt_long(argc, argv, "gsc:o:i:k:b:",
|
||||
long_options, &option_index)) >= 0) {
|
||||
switch (opt) {
|
||||
case 'g':
|
||||
myopts.get_mode = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
myopts.set_mode = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
myopts.create_mode = true;
|
||||
assert(optarg);
|
||||
if (!*optarg || !parse_creation_param(optarg, &myopts.create_param)) {
|
||||
fprintf(stderr, "error: invalid creation parameter: %s\n", optarg);
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
myopts.output_fn = optarg;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (!opt_props.set_new_value(
|
||||
GoogleBinaryBlockUtil::PROP_HWID, optarg ? optarg : "")) {
|
||||
fprintf(stderr, "error: cannot assign multiple HWID parameters\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
if (!opt_props.set_new_value(
|
||||
GoogleBinaryBlockUtil::PROP_ROOTKEY, optarg)) {
|
||||
fprintf(stderr, "error: cannot assign multiple rootkey parameters\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
if (!opt_props.set_new_value(
|
||||
GoogleBinaryBlockUtil::PROP_BMPFV, optarg)) {
|
||||
fprintf(stderr, "error: cannot assign multiple bmpfv parameters\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if (!opt_props.set_new_value(
|
||||
GoogleBinaryBlockUtil::PROP_RCVKEY, optarg)) {
|
||||
fprintf(stderr,
|
||||
"error: cannot assign multiple recovery_key parameters\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
char *endptr = optarg;
|
||||
|
||||
if (optarg) {
|
||||
flags = strtoul(optarg, &endptr, 0);
|
||||
if (endptr == optarg) {
|
||||
fprintf(stderr, "error: invalid --flags value\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt_props.set_new_value(GoogleBinaryBlockUtil::PROP_FLAGS,
|
||||
optarg ? int2bytes(flags) : "")) {
|
||||
fprintf(stderr, "error: cannot assign multiple flags parameters\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case '?':
|
||||
fprintf(stderr, "error: unknown param: %c\n", opt);
|
||||
usagehelp_exit(myname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
// adjust non-dashed parameters
|
||||
if (myopts.output_fn.empty() && argc == 2) {
|
||||
myopts.output_fn = argv[1];
|
||||
argc--;
|
||||
}
|
||||
|
||||
// currently, the only parameter is 'input file'.
|
||||
if (argc == 1) {
|
||||
myopts.input_fn = argv[0];
|
||||
} else {
|
||||
fprintf(stderr, "error: unexpected parameters (%d)\n", argc);
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
|
||||
// stage: complete parameter parsing and checking
|
||||
err_stage++;
|
||||
if (myopts.create_mode) {
|
||||
if (myopts.get_mode || myopts.set_mode) {
|
||||
printf("error: please assign only one mode from get/set/create.\n");
|
||||
return err_stage;
|
||||
}
|
||||
if (!opt_props.empty() || myopts.create_param.empty()) {
|
||||
printf("error: creation parameter syntax error.\n");
|
||||
return err_stage;
|
||||
}
|
||||
if (myopts.output_fn.empty()) {
|
||||
myopts.output_fn = myopts.input_fn;
|
||||
}
|
||||
} else if (myopts.get_mode == myopts.set_mode) {
|
||||
if (myopts.get_mode) {
|
||||
printf("error: please assign either get or set mode.\n");
|
||||
return err_stage;
|
||||
} else {
|
||||
// enter 'get' mode by default, if not assigned.
|
||||
myopts.get_mode = true;
|
||||
}
|
||||
}
|
||||
if (myopts.get_mode && !myopts.output_fn.empty()) {
|
||||
printf("error: get-mode does not create output files.\n");
|
||||
return err_stage;
|
||||
}
|
||||
|
||||
if (myopts.create_mode) {
|
||||
if (!util.create_new(myopts.create_param))
|
||||
return err_stage;
|
||||
|
||||
assert(!myopts.output_fn.empty());
|
||||
if (!util.save_to_file(myopts.output_fn.c_str())) {
|
||||
printf("error: cannot create to file: %s\n", myopts.output_fn.c_str());
|
||||
return err_stage;
|
||||
} else {
|
||||
printf("successfully created new GBB to: %s\n", myopts.output_fn.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stage: load image files
|
||||
err_stage++;
|
||||
assert(!myopts.input_fn.empty());
|
||||
if (!util.load_from_file(myopts.input_fn.c_str())) {
|
||||
printf("error: cannot load valid BIOS file: %s\n", myopts.input_fn.c_str());
|
||||
return err_stage;
|
||||
}
|
||||
|
||||
// stage: processing by mode
|
||||
err_stage++;
|
||||
if (myopts.get_mode) {
|
||||
// get mode
|
||||
if (opt_props.empty()) // enable hwid by default
|
||||
opt_props.set_new_value(GoogleBinaryBlockUtil::PROP_HWID, "");
|
||||
|
||||
for (OptPropertyMap::const_iterator i = opt_props.begin();
|
||||
i != opt_props.end();
|
||||
i++) {
|
||||
if (i->first == GoogleBinaryBlockUtil::PROP_HWID ||
|
||||
i->first == GoogleBinaryBlockUtil::PROP_FLAGS) {
|
||||
if (!i->second.empty()) {
|
||||
printf("error: cannot assign value for --hwid/flags in --get.\n");
|
||||
usagehelp_exit(myname);
|
||||
}
|
||||
}
|
||||
export_property(i->first, i->second, util);
|
||||
}
|
||||
|
||||
} else {
|
||||
// set mode
|
||||
assert(myopts.set_mode);
|
||||
|
||||
if (opt_props.empty()) {
|
||||
printf("nothing to change. abort.\n");
|
||||
return err_stage;
|
||||
}
|
||||
|
||||
for (OptPropertyMap::const_iterator i = opt_props.begin();
|
||||
i != opt_props.end();
|
||||
i++) {
|
||||
bool source_as_file = true;
|
||||
|
||||
// the hwid/flags are assigned in command line parameters
|
||||
if (i->first == GoogleBinaryBlockUtil::PROP_HWID ||
|
||||
i->first == GoogleBinaryBlockUtil::PROP_FLAGS)
|
||||
source_as_file = false;
|
||||
|
||||
if (!import_property(i->first, i->second, source_as_file, &util)) {
|
||||
printf("error: cannot set properties. abort.\n");
|
||||
return err_stage;
|
||||
}
|
||||
}
|
||||
|
||||
// stage: write output
|
||||
err_stage++;
|
||||
|
||||
// use input filename (overwrite) by default
|
||||
if (myopts.output_fn.empty())
|
||||
myopts.output_fn = myopts.input_fn;
|
||||
|
||||
assert(!myopts.output_fn.empty());
|
||||
if (!util.save_to_file(myopts.output_fn.c_str())) {
|
||||
printf("error: cannot save to file: %s\n", myopts.output_fn.c_str());
|
||||
return err_stage;
|
||||
} else {
|
||||
printf("successfully saved new image to: %s\n", myopts.output_fn.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // FOR_LIBRARY
|
||||
Reference in New Issue
Block a user