aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Pursell <dpursell@google.com>2024-04-15 12:04:28 -0700
committerDavid Pursell <dpursell@google.com>2024-04-16 11:58:11 -0700
commit3a87e91e84eb6a1239dea14616d0082b3a0f2a1b (patch)
tree4ad3b99bb84f1384df3f36cdb0790dd3930a12de
parente4cdfd5d79a651ebfc5d64739ea60a50dde0f628 (diff)
downloadavb-3a87e91e84eb6a1239dea14616d0082b3a0f2a1b.tar.gz
libavb: refactor cert usage strings
Refactors the certificate usage strings: * pulls them into constants * adds a bit of documentation * adds a `--usage_for_unlock` arg to `avbtool make_certificate` The intent here is to avoid users having to deal with the usage strings directly, and to provide some context for why they contain the substrings "android.things" even though they aren't specific to the Android Things project. In addition, the usage hash values are now pre-calculated in the libavb_cert implementation. This isn't directly related to the "android.things" string, but seems worth doing while we're here: * reduces .a artifact sizes by 1-2KiB * should reduce runtime stack usage by 32 bytes for the cert APIs Bug: b/333078493 Test: atest libavb_host_unittest Change-Id: I041b7f1b45e14657da1c3897085bdcc249498f52
-rwxr-xr-xavbtool.py86
-rw-r--r--libavb_cert/avb_cert_validate.c40
-rwxr-xr-xtest/avb_cert_generate_test_data2
-rw-r--r--test/avbtool_unittest.cc33
4 files changed, 96 insertions, 65 deletions
diff --git a/avbtool.py b/avbtool.py
index 0ee708d..d923b31 100755
--- a/avbtool.py
+++ b/avbtool.py
@@ -51,6 +51,17 @@ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1
# Configuration for enabling logging of calls to avbtool.
AVB_INVOCATION_LOGFILE = os.environ.get('AVB_INVOCATION_LOGFILE')
+# Known values for certificate "usage" field. These values must match the
+# libavb_cert implementation.
+#
+# The "android.things" substring is only for historical reasons; these strings
+# are used for the general-purpose libavb_cert extension and are not specific
+# to the Android Things project. However, changing them would be a breaking
+# change so it's simpler to leave them as-is.
+CERT_USAGE_SIGNING = 'com.google.android.things.vboot'
+CERT_USAGE_INTERMEDIATE_AUTHORITY = 'com.google.android.things.vboot.ca'
+CERT_USAGE_UNLOCK = 'com.google.android.things.vboot.unlock'
+
class AvbError(Exception):
"""Application-specific errors.
@@ -3863,9 +3874,8 @@ class Avb(object):
raise AvbError('Adding hashtree_footer failed: {}.'.format(e)) from e
def make_certificate(self, output, authority_key_path, subject_key_path,
- subject_key_version, subject,
- is_intermediate_authority, usage, signing_helper,
- signing_helper_with_files):
+ subject_key_version, subject, usage,
+ signing_helper, signing_helper_with_files):
"""Implements the 'make_certificate' command.
Certificates are required for avb_cert extension public key metadata. They
@@ -3884,9 +3894,7 @@ class Avb(object):
of seconds since the epoch is used.
subject: A subject identifier. For Product Signing Key certificates this
should be the same Product ID found in the permanent attributes.
- is_intermediate_authority: True if the certificate is for an intermediate
- authority.
- usage: If not empty, overrides the cert usage with a hash of this value.
+ usage: Usage string whose SHA256 hash will be embedded in the certificate.
signing_helper: Program which signs a hash and returns the signature.
signing_helper_with_files: Same as signing_helper but uses files instead.
@@ -3899,10 +3907,6 @@ class Avb(object):
hasher = hashlib.sha256()
hasher.update(subject)
signed_data.extend(hasher.digest())
- if not usage:
- usage = 'com.google.android.things.vboot'
- if is_intermediate_authority:
- usage += '.ca'
hasher = hashlib.sha256()
hasher.update(usage.encode('ascii'))
signed_data.extend(hasher.digest())
@@ -3919,7 +3923,7 @@ class Avb(object):
output.write(signature)
def make_cert_permanent_attributes(self, output, root_authority_key_path,
- product_id):
+ product_id):
"""Implements the 'make_cert_permanent_attributes' command.
avb_cert permanent attributes are designed to be permanent for a
@@ -3943,7 +3947,7 @@ class Avb(object):
output.write(product_id)
def make_cert_metadata(self, output, intermediate_key_certificate,
- product_key_certificate):
+ product_key_certificate):
"""Implements the 'make_cert_metadata' command.
avb_cert metadata are included in vbmeta images to facilitate
@@ -3953,11 +3957,11 @@ class Avb(object):
Arguments:
output: Metadata will be written to this file on success.
intermediate_key_certificate: A certificate file as output by
- make_certificate with
- is_intermediate_authority set to true.
+ make_certificate with usage set to
+ CERT_USAGE_INTERMEDIATE_AUTHORITY.
product_key_certificate: A certificate file as output by
- make_certificate with
- is_intermediate_authority set to false.
+ make_certificate with usage set to
+ CERT_USAGE_SIGNING.
Raises:
AvbError: If an argument is incorrect.
@@ -3972,9 +3976,9 @@ class Avb(object):
output.write(product_key_certificate)
def make_cert_unlock_credential(self, output, intermediate_key_certificate,
- unlock_key_certificate, challenge_path,
- unlock_key_path, signing_helper,
- signing_helper_with_files):
+ unlock_key_certificate, challenge_path,
+ unlock_key_path, signing_helper,
+ signing_helper_with_files):
"""Implements the 'make_cert_unlock_credential' command.
avb_cert unlock credentials can be used to authorize the unlock of AVB
@@ -3987,13 +3991,11 @@ class Avb(object):
Arguments:
output: The credential will be written to this file on success.
intermediate_key_certificate: A certificate file as output by
- make_certificate with
- is_intermediate_authority set to true.
+ make_certificate with usage set to
+ CERT_USAGE_INTERMEDIATE_AUTHORITY.
unlock_key_certificate: A certificate file as output by
- make_certificate with
- is_intermediate_authority set to false and the
- usage set to
- 'com.google.android.things.vboot.unlock'.
+ make_certificate with usage set to
+ CERT_USAGE_UNLOCK.
challenge_path: [optional] A path to the challenge to sign.
unlock_key_path: [optional] A PEM file path with the unlock private key.
signing_helper: Program which signs a hash and returns the signature.
@@ -4675,14 +4677,25 @@ class AvbTool(object):
help=('Version of the subject key'),
type=parse_number,
required=False)
- sub_parser.add_argument('--subject_is_intermediate_authority',
- help=('Generate an intermediate authority '
- 'certificate'),
- action='store_true')
- sub_parser.add_argument('--usage',
- help=('Override usage with a hash of the provided '
- 'string'),
- required=False)
+ # We have 3 different usage modifying args for convenience, at most one of
+ # which can be provided since they all set the same usage field.
+ usage_group = sub_parser.add_mutually_exclusive_group(required=False)
+ usage_group.add_argument('--subject_is_intermediate_authority',
+ help=('Override usage with the value used for '
+ 'an intermediate authority'),
+ action='store_const',
+ const=CERT_USAGE_INTERMEDIATE_AUTHORITY,
+ required=False)
+ usage_group.add_argument('--usage',
+ help=('Override usage with a hash of the provided '
+ 'string'),
+ required=False),
+ usage_group.add_argument('--usage_for_unlock',
+ help=('Override usage with the value used for '
+ 'authenticated unlock'),
+ action='store_const',
+ const=CERT_USAGE_UNLOCK,
+ required=False),
sub_parser.add_argument('--authority_key',
help='Path to authority RSA private key file',
required=False)
@@ -4934,12 +4947,15 @@ class AvbTool(object):
def make_certificate(self, args):
"""Implements the 'make_certificate' sub-command."""
+ # argparse mutually exclusive group ensures that at most one of the usage
+ # args will exist. If none exist, default to signing usage.
+ usage = (args.subject_is_intermediate_authority or args.usage or
+ args.usage_for_unlock or CERT_USAGE_SIGNING)
self.avb.make_certificate(args.output, args.authority_key,
args.subject_key.name,
args.subject_key_version,
args.subject.read(),
- args.subject_is_intermediate_authority,
- args.usage,
+ usage,
args.signing_helper,
args.signing_helper_with_files)
diff --git a/libavb_cert/avb_cert_validate.c b/libavb_cert/avb_cert_validate.c
index 223f73b..490248c 100644
--- a/libavb_cert/avb_cert_validate.c
+++ b/libavb_cert/avb_cert_validate.c
@@ -29,6 +29,24 @@
#include <libavb/avb_sysdeps.h>
#include <libavb/avb_util.h>
+/* Pre-computed SHA256 hashes for the known usage strings.
+ * Usage strings must match certs generated by avbtool.py. */
+/* com.google.android.things.vboot */
+const uint8_t CERT_USAGE_HASH_SIGNING[AVB_SHA256_DIGEST_SIZE] = {
+ 0x75, 0x04, 0x7f, 0xe1, 0x5e, 0xd4, 0x99, 0x80, 0x2d, 0xfd, 0x77,
+ 0x26, 0x00, 0x61, 0x18, 0xef, 0x5b, 0x06, 0x58, 0x56, 0xf5, 0x9c,
+ 0xa7, 0xf4, 0xdc, 0x63, 0xe7, 0x59, 0xe6, 0x48, 0xf8, 0x16};
+/* com.google.android.things.vboot.ca */
+const uint8_t CERT_USAGE_HASH_INTERMEDIATE_AUTHORITY[AVB_SHA256_DIGEST_SIZE] = {
+ 0x04, 0xec, 0x7c, 0xc7, 0x42, 0x41, 0x76, 0x3b, 0xcc, 0x72, 0xe3,
+ 0x5e, 0xd3, 0x92, 0xdf, 0xd8, 0x2a, 0x6c, 0x51, 0xae, 0xa8, 0xec,
+ 0x6d, 0x43, 0x27, 0xc7, 0x0d, 0xf4, 0x53, 0x4b, 0x21, 0x5c};
+/* com.google.android.things.vboot.unlock */
+const uint8_t CERT_USAGE_HASH_UNLOCK[AVB_SHA256_DIGEST_SIZE] = {
+ 0x7b, 0x84, 0x6c, 0x4a, 0xfd, 0x85, 0x48, 0x8f, 0x42, 0x9b, 0x7a,
+ 0xcf, 0x93, 0xcf, 0x6a, 0xff, 0x5c, 0x50, 0x28, 0x1b, 0xbf, 0x9b,
+ 0xd7, 0xb0, 0x18, 0xa5, 0x24, 0x2a, 0x86, 0x0d, 0xe3, 0xf8};
+
/* The most recent unlock challenge generated. */
static uint8_t last_unlock_challenge[AVB_CERT_UNLOCK_CHALLENGE_SIZE];
static bool last_unlock_challenge_set = false;
@@ -55,11 +73,6 @@ static void sha512(const uint8_t* data,
avb_memcpy(hash, tmp, AVB_SHA512_DIGEST_SIZE);
}
-/* Computes the SHA256 |hash| of a NUL-terminated |str|. */
-static void sha256_str(const char* str, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
- sha256((const uint8_t*)str, avb_strlen(str), hash);
-}
-
/* Verifies structure and |expected_hash| of permanent |attributes|. */
static bool verify_permanent_attributes(
const AvbCertPermanentAttributes* attributes,
@@ -124,11 +137,10 @@ static bool verify_pik_certificate(
const AvbCertCertificate* certificate,
const uint8_t authority[AVB_CERT_PUBLIC_KEY_SIZE],
uint64_t minimum_version) {
- uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
-
- sha256_str("com.google.android.things.vboot.ca", expected_usage);
- if (!verify_certificate(
- certificate, authority, minimum_version, expected_usage)) {
+ if (!verify_certificate(certificate,
+ authority,
+ minimum_version,
+ CERT_USAGE_HASH_INTERMEDIATE_AUTHORITY)) {
avb_error("Invalid PIK certificate.\n");
return false;
}
@@ -142,11 +154,9 @@ static bool verify_psk_certificate(
uint64_t minimum_version,
const uint8_t product_id[AVB_CERT_PRODUCT_ID_SIZE]) {
uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
- uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
- sha256_str("com.google.android.things.vboot", expected_usage);
if (!verify_certificate(
- certificate, authority, minimum_version, expected_usage)) {
+ certificate, authority, minimum_version, CERT_USAGE_HASH_SIGNING)) {
avb_error("Invalid PSK certificate.\n");
return false;
}
@@ -167,11 +177,9 @@ static bool verify_puk_certificate(
uint64_t minimum_version,
const uint8_t product_id[AVB_CERT_PRODUCT_ID_SIZE]) {
uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
- uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
- sha256_str("com.google.android.things.vboot.unlock", expected_usage);
if (!verify_certificate(
- certificate, authority, minimum_version, expected_usage)) {
+ certificate, authority, minimum_version, CERT_USAGE_HASH_UNLOCK)) {
avb_error("Invalid PUK certificate.\n");
return false;
}
diff --git a/test/avb_cert_generate_test_data b/test/avb_cert_generate_test_data
index 5671523..38f1b8d 100755
--- a/test/avb_cert_generate_test_data
+++ b/test/avb_cert_generate_test_data
@@ -95,7 +95,7 @@ head -c 16 /dev/urandom > cert_unlock_challenge.bin
# Construct a PUK certificate.
${AVBTOOL} make_certificate --output=cert_puk_certificate.bin \
--subject=cert_product_id.bin --subject_key=testkey_cert_puk.pem \
- --usage=com.google.android.things.vboot.unlock --subject_key_version 42 \
+ --usage_for_unlock --subject_key_version 42 \
--authority_key=testkey_cert_pik.pem
# Construct an unlock credential.
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index 7e5a453..96e68e2 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -3511,20 +3511,27 @@ TEST_F(AvbToolTest, MakeCertPukCertificate) {
pubkey_path.value().c_str());
base::FilePath output_path = testdir_.Append("tmp_certificate.bin");
- EXPECT_COMMAND(0,
- "./avbtool.py make_certificate"
- " --subject test/data/cert_product_id.bin"
- " --subject_key %s"
- " --subject_key_version 42"
- " --usage com.google.android.things.vboot.unlock"
- " --authority_key test/data/testkey_cert_pik.pem"
- " --output %s",
- pubkey_path.value().c_str(),
- output_path.value().c_str());
- EXPECT_COMMAND(0,
- "diff test/data/cert_puk_certificate.bin %s",
- output_path.value().c_str());
+ // Test with both legacy manual unlock --usage as well as --usage_for_unlock.
+ std::string usage_args[] = {"--usage com.google.android.things.vboot.unlock",
+ "--usage_for_unlock"};
+ for (const auto& usage : usage_args) {
+ EXPECT_COMMAND(0,
+ "./avbtool.py make_certificate"
+ " --subject test/data/cert_product_id.bin"
+ " --subject_key %s"
+ " --subject_key_version 42"
+ " %s"
+ " --authority_key test/data/testkey_cert_pik.pem"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ usage.c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/cert_puk_certificate.bin %s",
+ output_path.value().c_str());
+ }
}
TEST_F(AvbToolTest, MakeCertPermanentAttributes) {