Implement AVB Android Things eXtension (ATX) am: 147b08db62
am: 46bf7f29ff
Change-Id: Ib533722403e33df3f465b27469a554ebb465a2d5
diff --git a/Android.mk b/Android.mk
index b474fa1..9ec6d4d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,6 +118,20 @@
libavb_ab/avb_ab_flow.c
include $(BUILD_HOST_STATIC_LIBRARY)
+# Build libavb_atx for the host (for unit tests).
+include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_atx_host
+LOCAL_REQUIRED_MODULES := libavb_host
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags) -fno-stack-protector -DAVB_ENABLE_DEBUG -DAVB_COMPILATION
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := \
+ libavb_atx/avb_atx_validate.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
include $(CLEAR_VARS)
LOCAL_MODULE := libavb_host_sysdeps
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
@@ -132,7 +146,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := libavb_host_unittest
-LOCAL_REQUIRED_MODULES := simg2img img2simg
+LOCAL_REQUIRED_MODULES := simg2img img2simg avbtool
LOCAL_MODULE_HOST_OS := linux
LOCAL_CPP_EXTENSION := .cc
LOCAL_CLANG := true
@@ -143,12 +157,15 @@
libavb_host \
libavb_host_sysdeps \
libavb_ab_host \
+ libavb_atx_host \
libgmock_host \
libgtest_host
LOCAL_SHARED_LIBRARIES := \
- libchrome
+ libchrome \
+ libcrypto-host
LOCAL_SRC_FILES := \
test/avb_ab_flow_unittest.cc \
+ test/avb_atx_validate_unittest.cc \
test/avb_slot_verify_unittest.cc \
test/avb_unittest_util.cc \
test/avb_util_unittest.cc \
@@ -159,6 +176,18 @@
include $(BUILD_HOST_NATIVE_TEST)
include $(CLEAR_VARS)
+LOCAL_MODULE := libavb_host_user_code_test
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(avb_common_cflags)
+LOCAL_CPPFLAGS := $(avb_common_cppflags)
+LOCAL_LDFLAGS := $(avb_common_ldflags)
+LOCAL_SRC_FILES := test/user_code_test.cc
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
LOCAL_MODULE := bootctrl.avb
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_REQUIRED_MODULES := libavb
diff --git a/README b/README
index 2cd32d7..b5156b5 100644
--- a/README
+++ b/README
@@ -64,6 +64,11 @@
An implementation of a boot_control HAL using AVB-specific A/B
metadata is also provided.
+Android Things has specific requirements and validation logic for the
+vbmeta public key. An extension is provided in libavb_atx which performs
+this validation as an implementatio of libavb's public key validation
+operation (see avb_validate_vbmeta_public_key in avb_ops.h).
+
-- FILES AND DIRECTORIES
libavb/
@@ -85,6 +90,9 @@
An A/B implementation for use in boot loaders. This is built on top
of libavb.
+ libavb_atx/
+
+ An Android Things Extension for validating public key metadata.
boot_control/
@@ -103,7 +111,7 @@
test/
- Contains unit tests for abvtool, libavb, and libavb_ab.
+ Contains unit tests for abvtool, libavb, libavb_ab, and libavb_atx.
-- AUDIENCE AND PORTABILITY NOTES
@@ -269,7 +277,7 @@
e.g. derive AVB_pk. Both AVB_pk and AVB_pkmd is passed to the
bootloader for validation as part of verifying a slot.
-To prevent rollback attakcs, the rollback index should be increased on
+To prevent rollback attacks, the rollback index should be increased on
a regular basis. The rollback index can be set with the
BOARD_AVB_ROLLBACK_INDEX variable:
diff --git a/avbtool b/avbtool
index 328f927..4b1ed45 100755
--- a/avbtool
+++ b/avbtool
@@ -33,6 +33,7 @@
import subprocess
import sys
import tempfile
+import time
import Crypto.PublicKey.RSA
@@ -373,6 +374,41 @@
raise AvbError('Unknown algorithm type {}'.format(alg_type))
+def raw_sign(signing_helper, algorithm_name, key_path, raw_data_to_sign):
+ """Computes a raw RSA signature using |signing_helper| or openssl.
+
+ Arguments:
+ signing_helper: Program which signs a hash and returns the signature.
+ algorithm_name: The algorithm name as per the ALGORITHMS dict.
+ key_path: Path to the private key file. Must be PEM format.
+ raw_data_to_sign: Data to sign (bytearray or str expected).
+
+ Returns:
+ A bytearray containing the signature.
+
+ Raises:
+ Exception: If an error occurs.
+ """
+ p = None
+ if signing_helper is not None:
+ p = subprocess.Popen(
+ [signing_helper, algorithm_name, key_path],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ else:
+ p = subprocess.Popen(
+ ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (pout, perr) = p.communicate(str(raw_data_to_sign))
+ retcode = p.wait()
+ if retcode != 0:
+ raise AvbError('Error signing: {}'.format(perr))
+ return bytearray(pout)
+
+
class ImageChunk(object):
"""Data structure used for representing chunks in Android sparse files.
@@ -2030,25 +2066,9 @@
binary_hash.extend(ha.digest())
# Calculate the signature.
- p = None
- if signing_helper is not None:
- p = subprocess.Popen(
- [signing_helper, algorithm_name, key_path],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- else:
- p = subprocess.Popen(
- ['openssl', 'rsautl', '-sign', '-inkey', key_path, '-raw'],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
padding_and_hash = str(bytearray(alg.padding)) + binary_hash
- (pout, perr) = p.communicate(padding_and_hash)
- retcode = p.wait()
- if retcode != 0:
- raise AvbError('Error signing: {}'.format(perr))
- binary_signature.extend(pout)
+ binary_signature.extend(raw_sign(signing_helper, algorithm_name, key_path,
+ padding_and_hash))
# Generate Authentication data block.
auth_data_blob = bytearray()
@@ -2412,6 +2432,119 @@
image.truncate(original_image_size)
raise
+ def make_atx_certificate(self, output, authority_key_path, subject_key,
+ subject_key_version, subject,
+ is_intermediate_authority, signing_helper):
+ """Implements the 'make_atx_certificate' command.
+
+ Android Things certificates are required for Android Things public key
+ metadata. They chain the vbmeta signing key for a particular product back to
+ a fused, permanent root key. These certificates are fixed-length and fixed-
+ format with the explicit goal of not parsing ASN.1 in bootloader code.
+
+ Arguments:
+ output: Certificate will be written to this file on success.
+ authority_key_path: A PEM file path with the authority private key.
+ If None, then a certificate will be created without a
+ signature. The signature can be created out-of-band
+ and appended.
+ subject_key: A PEM or DER subject public key.
+ subject_key_version: A 64-bit version value. If this is None, the number
+ 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.
+ signing_helper: Program which signs a hash and returns the signature.
+ """
+ signed_data = bytearray()
+ signed_data.extend(struct.pack('<I', 1)) # Format Version
+ signed_data.extend(
+ encode_rsa_key(Crypto.PublicKey.RSA.importKey(subject_key)))
+ hasher = hashlib.sha256()
+ hasher.update(subject)
+ signed_data.extend(hasher.digest())
+ usage = 'com.google.android.things.vboot'
+ if is_intermediate_authority:
+ usage += '.ca'
+ hasher = hashlib.sha256()
+ hasher.update(usage)
+ signed_data.extend(hasher.digest())
+ if not subject_key_version:
+ subject_key_version = int(time.time())
+ signed_data.extend(struct.pack('<Q', subject_key_version))
+ signature = bytearray()
+ if authority_key_path:
+ padding_and_hash = bytearray()
+ algorithm_name = None
+ hasher = None
+ if is_intermediate_authority:
+ hasher = hashlib.sha512()
+ algorithm_name = 'SHA512_RSA4096'
+ else:
+ hasher = hashlib.sha256()
+ algorithm_name = 'SHA256_RSA2048'
+ padding_and_hash.extend(ALGORITHMS[algorithm_name].padding)
+ hasher.update(signed_data)
+ padding_and_hash.extend(hasher.digest())
+ signature.extend(raw_sign(signing_helper, algorithm_name,
+ authority_key_path, padding_and_hash))
+ output.write(signed_data)
+ output.write(signature)
+
+ def make_atx_permanent_attributes(self, output, root_authority_key,
+ product_id):
+ """Implements the 'make_atx_permanent_attributes' command.
+
+ Android Things permanent attributes are designed to be permanent for a
+ particular product and a hash of these attributes should be fused into
+ hardware to enforce this.
+
+ Arguments:
+ output: Attributes will be written to this file on success.
+ root_authority_key: A PEM or DER public key for the root authority.
+ product_id: A 16-byte Product ID.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ if len(product_id) != 16:
+ raise AvbError('Invalid Product ID length.')
+ output.write(struct.pack('<I', 1)) # Format Version
+ write_rsa_key(output, Crypto.PublicKey.RSA.importKey(root_authority_key))
+ output.write(product_id)
+
+ def make_atx_metadata(self, output, intermediate_key_certificate,
+ product_key_certificate, google_key_version):
+ """Implements the 'make_atx_metadata' command.
+
+ Android Things metadata are included in vbmeta images to facilitate
+ verification. The output of this command can be used as the
+ public_key_metadata argument to other commands.
+
+ Arguments:
+ output: Metadata will be written to this file on success.
+ intermediate_key_certificate: A certificate file as output by
+ make_atx_certificate with
+ is_intermediate_authority set to true.
+ product_key_certificate: A certificate file as output by
+ make_atx_certificate with
+ is_intermediate_authority set to false.
+ google_key_version: The version of the Google Signing Key used in the
+ associated vbmeta image.
+
+ Raises:
+ AvbError: If an argument is incorrect.
+ """
+ if len(intermediate_key_certificate) != 1108:
+ raise AvbError('Invalid intermediate key certificate length.')
+ if len(product_key_certificate) != 852:
+ raise AvbError('Invalid product key certificate length.')
+ output.write(struct.pack('<I', 1)) # Format Version
+ output.write(intermediate_key_certificate)
+ output.write(product_key_certificate)
+ output.write(struct.pack('<Q', google_key_version))
+
def calc_hash_level_offsets(image_size, block_size, digest_size):
"""Calculate the offsets of all the hash-levels in a Merkle-tree.
@@ -2772,6 +2905,77 @@
default='15:7:0:14:7:0')
sub_parser.set_defaults(func=self.set_ab_metadata)
+ sub_parser = subparsers.add_parser(
+ 'make_atx_certificate',
+ help='Create an Android Things eXtension (ATX) certificate.')
+ sub_parser.add_argument('--output',
+ help='Write certificate to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--subject',
+ help=('Path to subject file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--subject_key',
+ help=('Path to subject RSA public key file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--subject_key_version',
+ 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('--authority_key',
+ help='Path to authority RSA private key file',
+ required=False)
+ sub_parser.add_argument('--signing_helper',
+ help='Path to helper used for signing',
+ metavar='APP',
+ default=None,
+ required=False)
+ sub_parser.set_defaults(func=self.make_atx_certificate)
+
+ sub_parser = subparsers.add_parser(
+ 'make_atx_permanent_attributes',
+ help='Create Android Things eXtension (ATX) permanent attributes.')
+ sub_parser.add_argument('--output',
+ help='Write attributes to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--root_authority_key',
+ help='Path to authority RSA public key file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--product_id',
+ help=('Path to Product ID file'),
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.set_defaults(func=self.make_atx_permanent_attributes)
+
+ sub_parser = subparsers.add_parser(
+ 'make_atx_metadata',
+ help='Create Android Things eXtension (ATX) metadata.')
+ sub_parser.add_argument('--output',
+ help='Write metadata to file',
+ type=argparse.FileType('wb'),
+ default=sys.stdout)
+ sub_parser.add_argument('--intermediate_key_certificate',
+ help='Path to intermediate key certificate file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--product_key_certificate',
+ help='Path to product key certificate file',
+ type=argparse.FileType('rb'),
+ required=True)
+ sub_parser.add_argument('--google_key_version',
+ help=('Version of the Google signing key'),
+ type=parse_number,
+ default=0)
+ sub_parser.set_defaults(func=self.make_atx_metadata)
+
args = parser.parse_args(argv[1:])
try:
args.func(args)
@@ -2838,6 +3042,28 @@
"""Implements the 'info_image' sub-command."""
self.avb.info_image(args.image.name, args.output)
+ def make_atx_certificate(self, args):
+ """Implements the 'make_atx_certificate' sub-command."""
+ self.avb.make_atx_certificate(args.output, args.authority_key,
+ args.subject_key.read(),
+ args.subject_key_version,
+ args.subject.read(),
+ args.subject_is_intermediate_authority,
+ args.signing_helper)
+
+ def make_atx_permanent_attributes(self, args):
+ """Implements the 'make_atx_permanent_attributes' sub-command."""
+ self.avb.make_atx_permanent_attributes(args.output,
+ args.root_authority_key.read(),
+ args.product_id.read())
+
+ def make_atx_metadata(self, args):
+ """Implements the 'make_atx_metadata' sub-command."""
+ self.avb.make_atx_metadata(args.output,
+ args.intermediate_key_certificate.read(),
+ args.product_key_certificate.read(),
+ args.google_key_version)
+
if __name__ == '__main__':
tool = AvbTool()
diff --git a/libavb/avb_crypto.h b/libavb/avb_crypto.h
index d241520..7e8d7e2 100644
--- a/libavb/avb_crypto.h
+++ b/libavb/avb_crypto.h
@@ -35,6 +35,21 @@
extern "C" {
#endif
+/* Size of a RSA-2048 signature. */
+#define AVB_RSA2048_NUM_BYTES 256
+
+/* Size of a RSA-4096 signature. */
+#define AVB_RSA4096_NUM_BYTES 512
+
+/* Size of a RSA-8192 signature. */
+#define AVB_RSA8192_NUM_BYTES 1024
+
+/* Size in bytes of a SHA-256 digest. */
+#define AVB_SHA256_DIGEST_SIZE 32
+
+/* Size in bytes of a SHA-512 digest. */
+#define AVB_SHA512_DIGEST_SIZE 64
+
/* Algorithms that can be used in the vbmeta image for
* verification. An algorithm consists of a hash type and a signature
* type.
diff --git a/libavb/avb_ops.h b/libavb/avb_ops.h
index 3d5e964..908c66c 100644
--- a/libavb/avb_ops.h
+++ b/libavb/avb_ops.h
@@ -66,6 +66,9 @@
/* Forward-declaration of operations in libavb_ab. */
struct AvbABOps;
+/* Forward-declaration of operations in libavb_atx. */
+struct AvbAtxOps;
+
/* High-level operations/functions/methods that are platform
* dependent.
*/
@@ -81,6 +84,11 @@
*/
struct AvbABOps* ab_ops;
+ /* If libavb_atx is used, this should point to the
+ * AvbAtxOps. Otherwise it must be set to NULL.
+ */
+ struct AvbAtxOps* atx_ops;
+
/* Reads |num_bytes| from offset |offset| from partition with name
* |partition| (NUL-terminated UTF-8 string). If |offset| is
* negative, its absolute value should be interpreted as the number
diff --git a/libavb/avb_rsa.h b/libavb/avb_rsa.h
index 7e81b81..c2dcf47 100644
--- a/libavb/avb_rsa.h
+++ b/libavb/avb_rsa.h
@@ -42,17 +42,9 @@
extern "C" {
#endif
+#include "avb_crypto.h"
#include "avb_sysdeps.h"
-/* Size of a RSA-2048 signature. */
-#define AVB_RSA2048_NUM_BYTES 256
-
-/* Size of a RSA-4096 signature. */
-#define AVB_RSA4096_NUM_BYTES 512
-
-/* Size of a RSA-8192 signature. */
-#define AVB_RSA8192_NUM_BYTES 1024
-
/* Using the key given by |key|, verify a RSA signature |sig| of
* length |sig_num_bytes| against an expected |hash| of length
* |hash_num_bytes|. The padding to expect must be passed in using
diff --git a/libavb/avb_sha.h b/libavb/avb_sha.h
index b925bd2..c5a6a4c 100644
--- a/libavb/avb_sha.h
+++ b/libavb/avb_sha.h
@@ -37,16 +37,12 @@
extern "C" {
#endif
+#include "avb_crypto.h"
#include "avb_sysdeps.h"
-/* Size in bytes of a SHA-256 digest. */
-#define AVB_SHA256_DIGEST_SIZE 32
-
/* Block size in bytes of a SHA-256 digest. */
#define AVB_SHA256_BLOCK_SIZE 64
-/* Size in bytes of a SHA-512 digest. */
-#define AVB_SHA512_DIGEST_SIZE 64
/* Block size in bytes of a SHA-512 digest. */
#define AVB_SHA512_BLOCK_SIZE 128
diff --git a/libavb_atx/avb_atx_ops.h b/libavb_atx/avb_atx_ops.h
new file mode 100644
index 0000000..e05ce99
--- /dev/null
+++ b/libavb_atx/avb_atx_ops.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_OPS_H_
+#define AVB_ATX_OPS_H_
+
+#include <libavb/libavb.h>
+
+#include "libavb_atx/avb_atx_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AvbAtxOps;
+typedef struct AvbAtxOps AvbAtxOps;
+
+/* An extension to AvbOps required by avb_atx_validate_vbmeta_public_key(). */
+struct AvbAtxOps {
+ /* Operations from libavb. */
+ AvbOps* ops;
+
+ /* Reads permanent |attributes| data. There are no restrictions on where this
+ * data is stored. On success, returns AVB_IO_RESULT_OK and populates
+ * |attributes|.
+ */
+ AvbIOResult (*read_permanent_attributes)(
+ AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes);
+
+ /* Reads a |hash| of permanent attributes. This hash MUST be retrieved from a
+ * permanently read-only location (e.g. fuses) when a device is LOCKED. On
+ * success, returned AVB_IO_RESULT_OK and populates |hash|.
+ */
+ AvbIOResult (*read_permanent_attributes_hash)(
+ AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_OPS_H_ */
diff --git a/libavb_atx/avb_atx_types.h b/libavb_atx/avb_atx_types.h
new file mode 100644
index 0000000..ae1b438
--- /dev/null
+++ b/libavb_atx/avb_atx_types.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_TYPES_H_
+#define AVB_ATX_TYPES_H_
+
+#include <libavb/libavb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Size in bytes of an Android Things product ID. */
+#define AVB_ATX_PRODUCT_ID_SIZE 16
+
+/* Size in bytes of a serialized public key with a 2048-bit modulus. */
+#define AVB_ATX_PUBLIC_KEY_SIZE_2048 (sizeof(AvbRSAPublicKeyHeader) + 512)
+
+/* Size in bytes of a serialized public key with a 4096-bit modulus. */
+#define AVB_ATX_PUBLIC_KEY_SIZE_4096 (sizeof(AvbRSAPublicKeyHeader) + 1024)
+
+/* Data structure of Android Things permanent attributes. */
+typedef struct AvbAtxPermanentAttributes {
+ uint32_t version;
+ uint8_t product_root_public_key[AVB_ATX_PUBLIC_KEY_SIZE_4096];
+ uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE];
+} AVB_ATTR_PACKED AvbAtxPermanentAttributes;
+
+/* Data structure of signed fields in an Android Things certificate. */
+typedef struct AvbAtxCertificateSignedData {
+ uint32_t version;
+ uint8_t public_key[AVB_ATX_PUBLIC_KEY_SIZE_2048];
+ uint8_t subject[AVB_SHA256_DIGEST_SIZE];
+ uint8_t usage[AVB_SHA256_DIGEST_SIZE];
+ uint64_t key_version;
+} AVB_ATTR_PACKED AvbAtxCertificateSignedData;
+
+/* Data structure of a certificate signed by a 4096-bit key. */
+typedef struct AvbAtxCertificate4096 {
+ AvbAtxCertificateSignedData signed_data;
+ uint8_t signature[AVB_RSA4096_NUM_BYTES];
+} AVB_ATTR_PACKED AvbAtxCertificate4096;
+
+/* Data structure of a certificate signed by a 2048-bit key. */
+typedef struct AvbAtxCertificate2048 {
+ AvbAtxCertificateSignedData signed_data;
+ uint8_t signature[AVB_RSA2048_NUM_BYTES];
+} AVB_ATTR_PACKED AvbAtxCertificate2048;
+
+/* Data structure of Android Things public key metadata in vbmeta. */
+typedef struct AvbAtxPublicKeyMetadata {
+ uint32_t version;
+ AvbAtxCertificate4096 product_intermediate_key_certificate;
+ AvbAtxCertificate2048 product_signing_key_certificate;
+ uint64_t google_signing_key_version;
+} AVB_ATTR_PACKED AvbAtxPublicKeyMetadata;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_TYPES_H_ */
diff --git a/libavb_atx/avb_atx_validate.c b/libavb_atx/avb_atx_validate.c
new file mode 100644
index 0000000..7a29d66
--- /dev/null
+++ b/libavb_atx/avb_atx_validate.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "libavb_atx/avb_atx_validate.h"
+
+#include "libavb/avb_rsa.h"
+#include "libavb/avb_sha.h"
+#include "libavb/avb_sysdeps.h"
+#include "libavb/avb_util.h"
+
+/* Computes the SHA256 |hash| of |length| bytes of |data|. */
+static void sha256(const uint8_t* data,
+ uint32_t length,
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ AvbSHA256Ctx context;
+ avb_sha256_init(&context);
+ avb_sha256_update(&context, data, length);
+ uint8_t* tmp = avb_sha256_final(&context);
+ avb_memcpy(hash, tmp, AVB_SHA256_DIGEST_SIZE);
+}
+
+/* Computes the SHA512 |hash| of |length| bytes of |data|. */
+static void sha512(const uint8_t* data,
+ uint32_t length,
+ uint8_t hash[AVB_SHA512_DIGEST_SIZE]) {
+ AvbSHA512Ctx context;
+ avb_sha512_init(&context);
+ avb_sha512_update(&context, data, length);
+ uint8_t* tmp = avb_sha512_final(&context);
+ 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 AvbAtxPermanentAttributes* attributes,
+ uint8_t expected_hash[AVB_SHA256_DIGEST_SIZE]) {
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE];
+
+ if (attributes->version != 1) {
+ avb_error("Unsupported permanent attributes version.\n");
+ return false;
+ }
+ sha256((const uint8_t*)attributes, sizeof(AvbAtxPermanentAttributes), hash);
+ if (0 != avb_safe_memcmp(hash, expected_hash, AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid permanent attributes.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Verifies signature and fields of a PIK certificate. */
+static bool verify_pik_certificate(
+ AvbAtxCertificate4096* certificate,
+ uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE_4096],
+ uint64_t minimum_version) {
+ const AvbAlgorithmData* algorithm_data;
+ uint8_t certificate_hash[AVB_SHA512_DIGEST_SIZE];
+ uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
+
+ if (certificate->signed_data.version != 1) {
+ avb_error("Unsupported PIK certificate format.\n");
+ return false;
+ }
+ algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA512_RSA4096);
+ sha512((const uint8_t*)&certificate->signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ certificate_hash);
+ if (!avb_rsa_verify(authority,
+ AVB_ATX_PUBLIC_KEY_SIZE_4096,
+ certificate->signature,
+ AVB_RSA4096_NUM_BYTES,
+ certificate_hash,
+ AVB_SHA512_DIGEST_SIZE,
+ algorithm_data->padding,
+ algorithm_data->padding_len)) {
+ avb_error("Invalid PIK certificate signature.\n");
+ return false;
+ }
+ sha256_str("com.google.android.things.vboot.ca", expected_usage);
+ if (0 != avb_safe_memcmp(certificate->signed_data.usage,
+ expected_usage,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid PIK certificate usage.\n");
+ return false;
+ }
+ if (certificate->signed_data.key_version < minimum_version) {
+ avb_error("PIK rollback detected.\n");
+ return false;
+ }
+ return true;
+}
+
+/* Verifies signature and fields of a PSK certificate. */
+static bool verify_psk_certificate(
+ AvbAtxCertificate2048* certificate,
+ uint8_t authority[AVB_ATX_PUBLIC_KEY_SIZE_2048],
+ uint64_t minimum_version,
+ uint8_t product_id[AVB_ATX_PRODUCT_ID_SIZE]) {
+ const AvbAlgorithmData* algorithm_data;
+ uint8_t certificate_hash[AVB_SHA256_DIGEST_SIZE];
+ uint8_t expected_subject[AVB_SHA256_DIGEST_SIZE];
+ uint8_t expected_usage[AVB_SHA256_DIGEST_SIZE];
+ if (certificate->signed_data.version != 1) {
+ avb_error("Unsupported PSK certificate format.\n");
+ return false;
+ }
+ algorithm_data = avb_get_algorithm_data(AVB_ALGORITHM_TYPE_SHA256_RSA2048);
+ sha256((const uint8_t*)&certificate->signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ certificate_hash);
+ if (!avb_rsa_verify(authority,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ certificate->signature,
+ AVB_RSA2048_NUM_BYTES,
+ certificate_hash,
+ AVB_SHA256_DIGEST_SIZE,
+ algorithm_data->padding,
+ algorithm_data->padding_len)) {
+ avb_error("Invalid PSK certificate signature.\n");
+ return false;
+ }
+ sha256(product_id, AVB_ATX_PRODUCT_ID_SIZE, expected_subject);
+ if (0 != avb_safe_memcmp(certificate->signed_data.subject,
+ expected_subject,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Product ID mismatch.\n");
+ return false;
+ }
+ sha256_str("com.google.android.things.vboot", expected_usage);
+ if (0 != avb_safe_memcmp(certificate->signed_data.usage,
+ expected_usage,
+ AVB_SHA256_DIGEST_SIZE)) {
+ avb_error("Invalid PSK certificate usage.\n");
+ return false;
+ }
+ if (certificate->signed_data.key_version < minimum_version) {
+ avb_error("PSK rollback detected.\n");
+ return false;
+ }
+ return true;
+}
+
+AvbIOResult avb_atx_validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted) {
+ AvbIOResult result = AVB_IO_RESULT_OK;
+ AvbAtxPermanentAttributes permanent_attributes;
+ uint8_t permanent_attributes_hash[AVB_SHA256_DIGEST_SIZE];
+ AvbAtxPublicKeyMetadata metadata;
+ uint64_t minimum_version;
+
+ /* Be pessimistic so we can exit early without having to remember to clear. */
+ *out_is_trusted = false;
+
+ /* Read and verify permanent attributes. */
+ result = ops->atx_ops->read_permanent_attributes(ops->atx_ops,
+ &permanent_attributes);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read permanent attributes.\n");
+ return result;
+ }
+ result = ops->atx_ops->read_permanent_attributes_hash(
+ ops->atx_ops, permanent_attributes_hash);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read permanent attributes hash.\n");
+ return result;
+ }
+ if (!verify_permanent_attributes(&permanent_attributes,
+ permanent_attributes_hash)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Sanity check public key metadata. */
+ if (public_key_metadata_length != sizeof(AvbAtxPublicKeyMetadata)) {
+ avb_error("Invalid public key metadata.\n");
+ return AVB_IO_RESULT_OK;
+ }
+ avb_memcpy(&metadata, public_key_metadata, sizeof(AvbAtxPublicKeyMetadata));
+ if (metadata.version != 1) {
+ avb_error("Unsupported public key metadata.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PIK certificate. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_PIK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read PIK minimum version.\n");
+ return result;
+ }
+ if (!verify_pik_certificate(&metadata.product_intermediate_key_certificate,
+ permanent_attributes.product_root_public_key,
+ minimum_version)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PSK certificate. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_PSK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read PSK minimum version.\n");
+ return result;
+ }
+ if (!verify_psk_certificate(
+ &metadata.product_signing_key_certificate,
+ metadata.product_intermediate_key_certificate.signed_data.public_key,
+ minimum_version,
+ permanent_attributes.product_id)) {
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the PSK is the same key that verified vbmeta. */
+ if (public_key_length != AVB_ATX_PUBLIC_KEY_SIZE_2048) {
+ avb_error("Public key length mismatch.\n");
+ return AVB_IO_RESULT_OK;
+ }
+ if (0 != avb_safe_memcmp(
+ metadata.product_signing_key_certificate.signed_data.public_key,
+ public_key_data,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048)) {
+ avb_error("Public key mismatch.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ /* Verify the GSK minimum version. */
+ result = ops->read_rollback_index(
+ ops, AVB_ATX_GSK_VERSION_LOCATION, &minimum_version);
+ if (result != AVB_IO_RESULT_OK) {
+ avb_error("Failed to read GSK minimum version.\n");
+ return result;
+ }
+ if (metadata.google_signing_key_version < minimum_version) {
+ avb_error("Google signing key rollback detected.\n");
+ return AVB_IO_RESULT_OK;
+ }
+
+ *out_is_trusted = true;
+ return AVB_IO_RESULT_OK;
+}
diff --git a/libavb_atx/avb_atx_validate.h b/libavb_atx/avb_atx_validate.h
new file mode 100644
index 0000000..07c154e
--- /dev/null
+++ b/libavb_atx/avb_atx_validate.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#if !defined(AVB_INSIDE_LIBAVB_ATX_H) && !defined(AVB_COMPILATION)
+#error \
+ "Never include this file directly, include libavb_atx/libavb_atx.h instead."
+#endif
+
+#ifndef AVB_ATX_VALIDATE_H_
+#define AVB_ATX_VALIDATE_H_
+
+#include "libavb_atx/avb_atx_ops.h"
+#include "libavb_atx/avb_atx_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Rollback index locations for Android Things key versions. */
+#define AVB_ATX_PIK_VERSION_LOCATION 0x1000
+#define AVB_ATX_PSK_VERSION_LOCATION 0x1001
+#define AVB_ATX_GSK_VERSION_LOCATION 0x1002
+
+/* An implementation of validate_vbmeta_public_key for Android Things. See
+ * libavb/avb_ops.h for details on validate_vbmeta_public_key in general. This
+ * implementation uses the metadata expected with Android Things vbmeta images
+ * to perform validation on the public key. The ATX ops must be implemented.
+ * That is, |ops->atx_ops| must be valid.
+ *
+ * There are a multiple values that need verification:
+ * - Permanent Product Attributes: A hash of these attributes is fused into
+ * hardware. Consistency is checked.
+ * - Product Root Key (PRK): This key is provided in permanent attributes and
+ * is the root authority for all Android Things
+ * products.
+ * - Product Intermediate Key (PIK): This key is a rotated intermediary. It is
+ * certified by the PRK.
+ * - Product Signing Key (PSK): This key is a rotated authority for a specific
+ * Android Things product. It is certified by a
+ * PIK and must match |public_key_data|.
+ * - Google Signing Key (GSK): This key is a rotated authority for system and
+ * boot partitions built by Google. The key is
+ * already verified as part of vbmeta so only the
+ * key version needs verification here.
+ * - Product ID: This value is provided in permanent attributes and is unique
+ * to a specific Android Things product. This value must match
+ * the subject of the PSK certificate.
+ */
+AvbIOResult avb_atx_validate_vbmeta_public_key(
+ AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_is_trusted);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AVB_ATX_VALIDATE_H_ */
diff --git a/libavb_atx/libavb_atx.h b/libavb_atx/libavb_atx.h
new file mode 100644
index 0000000..839c0af
--- /dev/null
+++ b/libavb_atx/libavb_atx.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef LIBAVB_ATX_H_
+#define LIBAVB_ATX_H_
+
+#include <libavb/libavb.h>
+
+/* The AVB_INSIDE_LIBAVB_ATX_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+
+#define AVB_INSIDE_LIBAVB_ATX_H
+#include "avb_atx_ops.h"
+#include "avb_atx_types.h"
+#include "avb_atx_validate.h"
+#undef AVB_INSIDE_LIBAVB_ATX_H
+
+#endif /* LIBAVB_ATX_H_ */
diff --git a/test/avb_atx_validate_unittest.cc b/test/avb_atx_validate_unittest.cc
new file mode 100644
index 0000000..6efa856
--- /dev/null
+++ b/test/avb_atx_validate_unittest.cc
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <base/files/file_util.h>
+#include <gtest/gtest.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#include <libavb_atx/libavb_atx.h>
+
+#include "avb_unittest_util.h"
+#include "fake_avb_ops.h"
+
+namespace {
+
+const char kMetadataPath[] = "test/data/atx_metadata.bin";
+const char kPermanentAttributesPath[] =
+ "test/data/atx_permanent_attributes.bin";
+const char kPRKPrivateKeyPath[] = "test/data/testkey_rsa4096.pem";
+const char kPIKPrivateKeyPath[] = "test/data/testkey_rsa2048.pem";
+
+class ScopedRSA {
+ public:
+ ScopedRSA(const char* pem_key_path) {
+ FILE* file = fopen(pem_key_path, "r");
+ rsa_ = PEM_read_RSAPrivateKey(file, nullptr, nullptr, nullptr);
+ fclose(file);
+ }
+
+ ~ScopedRSA() {
+ if (rsa_) {
+ RSA_free(rsa_);
+ }
+ }
+
+ // PKCS #1 v1.5 signature using SHA512 if |rsa_| is 4096 bits, SHA256
+ // otherwise. Returns true on success.
+ bool Sign(const void* data_to_sign, size_t length, uint8_t signature[]) {
+ bool use_sha512 = (RSA_size(rsa_) == AVB_RSA4096_NUM_BYTES);
+ uint8_t digest[AVB_SHA512_DIGEST_SIZE];
+ unsigned int digest_size =
+ use_sha512 ? AVB_SHA512_DIGEST_SIZE : AVB_SHA256_DIGEST_SIZE;
+ const unsigned char* data_to_sign_buf =
+ reinterpret_cast<const unsigned char*>(data_to_sign);
+ if (use_sha512) {
+ SHA512(data_to_sign_buf, length, digest);
+ } else {
+ SHA256(data_to_sign_buf, length, digest);
+ }
+ unsigned int signature_length = 0;
+ return (1 == RSA_sign(use_sha512 ? NID_sha512 : NID_sha256,
+ digest,
+ digest_size,
+ signature,
+ &signature_length,
+ rsa_));
+ }
+
+ private:
+ RSA* rsa_;
+};
+
+} /* namespace */
+
+namespace avb {
+
+class AvbAtxValidateTest : public ::testing::Test, public FakeAvbOpsDelegate {
+ public:
+ ~AvbAtxValidateTest() override {}
+
+ void SetUp() override {
+ ReadDefaultData();
+ ops_.set_delegate(this);
+ ops_.set_permanent_attributes(attributes_);
+ ops_.set_stored_rollback_indexes({{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ }
+
+ // FakeAvbOpsDelegate methods.
+ AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) override {
+ if ((fail_read_pik_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_PIK_VERSION_LOCATION) ||
+ (fail_read_psk_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_PSK_VERSION_LOCATION) ||
+ (fail_read_gsk_rollback_index_ &&
+ rollback_index_slot == AVB_ATX_GSK_VERSION_LOCATION)) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_rollback_index(
+ ops, rollback_index_slot, out_rollback_index);
+ }
+
+ AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) override {
+ // Expect method not used.
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ }
+
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override {
+ if (fail_read_permanent_attributes_) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_permanent_attributes(attributes);
+ }
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override {
+ if (fail_read_permanent_attributes_hash_) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+ return ops_.read_permanent_attributes_hash(hash);
+ }
+
+ protected:
+ virtual AvbIOResult Validate(bool* is_trusted) {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ is_trusted);
+ }
+
+ void SignPIKCertificate() {
+ memset(metadata_.product_intermediate_key_certificate.signature,
+ 0,
+ AVB_RSA4096_NUM_BYTES);
+ ScopedRSA key(kPRKPrivateKeyPath);
+ ASSERT_TRUE(
+ key.Sign(&metadata_.product_intermediate_key_certificate.signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ metadata_.product_intermediate_key_certificate.signature));
+ }
+
+ void SignPSKCertificate() {
+ memset(metadata_.product_signing_key_certificate.signature,
+ 0,
+ AVB_RSA2048_NUM_BYTES);
+ ScopedRSA key(kPIKPrivateKeyPath);
+ ASSERT_TRUE(key.Sign(&metadata_.product_signing_key_certificate.signed_data,
+ sizeof(AvbAtxCertificateSignedData),
+ metadata_.product_signing_key_certificate.signature));
+ }
+
+ FakeAvbOps ops_;
+ AvbAtxPermanentAttributes attributes_;
+ AvbAtxPublicKeyMetadata metadata_;
+ bool fail_read_permanent_attributes_{false};
+ bool fail_read_permanent_attributes_hash_{false};
+ bool fail_read_pik_rollback_index_{false};
+ bool fail_read_psk_rollback_index_{false};
+ bool fail_read_gsk_rollback_index_{false};
+
+ private:
+ void ReadDefaultData() {
+ std::string tmp;
+ ASSERT_TRUE(base::ReadFileToString(base::FilePath(kMetadataPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPublicKeyMetadata));
+ memcpy(&metadata_, tmp.data(), tmp.size());
+ ASSERT_TRUE(
+ base::ReadFileToString(base::FilePath(kPermanentAttributesPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPermanentAttributes));
+ memcpy(&attributes_, tmp.data(), tmp.size());
+ }
+};
+
+TEST_F(AvbAtxValidateTest, Success) {
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, SuccessAfterNewSign) {
+ std::string old_pik_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_intermediate_key_certificate.signature),
+ AVB_RSA4096_NUM_BYTES);
+ std::string old_psk_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_signing_key_certificate.signature),
+ AVB_RSA2048_NUM_BYTES);
+ SignPIKCertificate();
+ SignPSKCertificate();
+ std::string new_pik_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_intermediate_key_certificate.signature),
+ AVB_RSA4096_NUM_BYTES);
+ std::string new_psk_sig(
+ reinterpret_cast<char*>(
+ metadata_.product_signing_key_certificate.signature),
+ AVB_RSA2048_NUM_BYTES);
+ EXPECT_EQ(old_pik_sig, new_pik_sig);
+ EXPECT_EQ(old_psk_sig, new_psk_sig);
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPermamentAttributes) {
+ fail_read_permanent_attributes_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPermamentAttributesHash) {
+ fail_read_permanent_attributes_hash_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPermanentAttributesVersion) {
+ attributes_.version = 25;
+ ops_.set_permanent_attributes(attributes_);
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PermanentAttributesHashMismatch) {
+ ops_.set_permanent_attributes_hash("bad_hash");
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture with parameterized metadata length.
+class AvbAtxValidateTestWithMetadataLength
+ : public AvbAtxValidateTest,
+ public ::testing::WithParamInterface<size_t> {
+ protected:
+ AvbIOResult Validate(bool* is_trusted) override {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ GetParam(),
+ is_trusted);
+ }
+};
+
+TEST_P(AvbAtxValidateTestWithMetadataLength, InvalidMetadataLength) {
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// Test a bunch of invalid metadata length values.
+INSTANTIATE_TEST_CASE_P(P,
+ AvbAtxValidateTestWithMetadataLength,
+ ::testing::Values(0,
+ 1,
+ sizeof(AvbAtxPublicKeyMetadata) - 1,
+ sizeof(AvbAtxPublicKeyMetadata) + 1,
+ -1));
+
+TEST_F(AvbAtxValidateTest, UnsupportedMetadataVersion) {
+ metadata_.version = 25;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPIKRollbackIndex) {
+ fail_read_pik_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPIKCertificateVersion) {
+ metadata_.product_intermediate_key_certificate.signed_data.version = 25;
+ SignPIKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedSubjectPublicKey) {
+ metadata_.product_intermediate_key_certificate.signed_data.public_key[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedSubject) {
+ metadata_.product_intermediate_key_certificate.signed_data.subject[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedUsage) {
+ metadata_.product_intermediate_key_certificate.signed_data.usage[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_ModifiedKeyVersion) {
+ metadata_.product_intermediate_key_certificate.signed_data.key_version ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPIKCert_BadSignature) {
+ metadata_.product_intermediate_key_certificate.signature[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKCertSubjectIgnored) {
+ metadata_.product_intermediate_key_certificate.signed_data.subject[0] ^= 1;
+ SignPIKCertificate();
+ bool is_trusted = false;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_TRUE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKCertUnexpectedUsage) {
+ metadata_.product_intermediate_key_certificate.signed_data.usage[0] ^= 1;
+ SignPIKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PIKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION,
+ metadata_.product_intermediate_key_certificate.signed_data.key_version +
+ 1},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadPSKRollbackIndex) {
+ fail_read_psk_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, UnsupportedPSKCertificateVersion) {
+ metadata_.product_signing_key_certificate.signed_data.version = 25;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedSubjectPublicKey) {
+ metadata_.product_signing_key_certificate.signed_data.public_key[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedSubject) {
+ metadata_.product_signing_key_certificate.signed_data.subject[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedUsage) {
+ metadata_.product_signing_key_certificate.signed_data.usage[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_ModifiedKeyVersion) {
+ metadata_.product_signing_key_certificate.signed_data.key_version ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, BadPSKCert_BadSignature) {
+ metadata_.product_signing_key_certificate.signature[0] ^= 1;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKCertUnexpectedSubject) {
+ metadata_.product_signing_key_certificate.signed_data.subject[0] ^= 1;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKCertUnexpectedUsage) {
+ metadata_.product_signing_key_certificate.signed_data.usage[0] ^= 1;
+ SignPSKCertificate();
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, PSKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION,
+ metadata_.product_signing_key_certificate.signed_data.key_version + 1},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture with parameterized public key length.
+class AvbAtxValidateTestWithPublicKeyLength
+ : public AvbAtxValidateTest,
+ public ::testing::WithParamInterface<size_t> {
+ protected:
+ AvbIOResult Validate(bool* is_trusted) override {
+ return avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ metadata_.product_signing_key_certificate.signed_data.public_key,
+ GetParam(),
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ is_trusted);
+ }
+};
+
+TEST_P(AvbAtxValidateTestWithPublicKeyLength, InvalidPublicKeyLength) {
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// Test a bunch of invalid public key length values.
+INSTANTIATE_TEST_CASE_P(P,
+ AvbAtxValidateTestWithPublicKeyLength,
+ ::testing::Values(0,
+ 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048 - 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048 + 1,
+ AVB_ATX_PUBLIC_KEY_SIZE_4096,
+ -1));
+
+TEST_F(AvbAtxValidateTest, PSKMismatch) {
+ uint8_t bad_key[AVB_ATX_PUBLIC_KEY_SIZE_2048] = {};
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK,
+ avb_atx_validate_vbmeta_public_key(
+ ops_.avb_ops(),
+ bad_key,
+ AVB_ATX_PUBLIC_KEY_SIZE_2048,
+ reinterpret_cast<const uint8_t*>(&metadata_),
+ sizeof(metadata_),
+ &is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, FailReadGSKRollbackIndex) {
+ fail_read_gsk_rollback_index_ = true;
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_ERROR_IO, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+TEST_F(AvbAtxValidateTest, GSKRollback) {
+ ops_.set_stored_rollback_indexes(
+ {{AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION,
+ metadata_.google_signing_key_version + 1}});
+ bool is_trusted = true;
+ EXPECT_EQ(AVB_IO_RESULT_OK, Validate(&is_trusted));
+ EXPECT_FALSE(is_trusted);
+}
+
+// A fixture for testing avb_slot_verify() with ATX.
+class AvbAtxSlotVerifyTest : public BaseAvbToolTest, public FakeAvbOpsDelegate {
+ public:
+ ~AvbAtxSlotVerifyTest() override = default;
+
+ void SetUp() override {
+ BaseAvbToolTest::SetUp();
+ ReadAtxDefaultData();
+ ops_.set_partition_dir(testdir_);
+ ops_.set_delegate(this);
+ ops_.set_permanent_attributes(attributes_);
+ ops_.set_stored_rollback_indexes({{0, 0},
+ {1, 0},
+ {2, 0},
+ {3, 0},
+ {AVB_ATX_PIK_VERSION_LOCATION, 0},
+ {AVB_ATX_PSK_VERSION_LOCATION, 0},
+ {AVB_ATX_GSK_VERSION_LOCATION, 0}});
+ ops_.set_stored_is_device_unlocked(false);
+ }
+
+ // FakeAvbOpsDelegate methods. All forward to FakeAvbOps default except for
+ // validate_vbmeta_public_key().
+ AvbIOResult read_from_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ void* buffer,
+ size_t* out_num_read) override {
+ return ops_.read_from_partition(
+ partition, offset, num_bytes, buffer, out_num_read);
+ }
+
+ AvbIOResult write_to_partition(const char* partition,
+ int64_t offset,
+ size_t num_bytes,
+ const void* buffer) override {
+ return ops_.write_to_partition(partition, offset, num_bytes, buffer);
+ }
+
+ AvbIOResult validate_vbmeta_public_key(AvbOps* ops,
+ const uint8_t* public_key_data,
+ size_t public_key_length,
+ const uint8_t* public_key_metadata,
+ size_t public_key_metadata_length,
+ bool* out_key_is_trusted) override {
+ // Send to ATX implementation.
+ ++num_atx_calls_;
+ return avb_atx_validate_vbmeta_public_key(ops_.avb_ops(),
+ public_key_data,
+ public_key_length,
+ public_key_metadata,
+ public_key_metadata_length,
+ out_key_is_trusted);
+ }
+
+ AvbIOResult read_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t* out_rollback_index) override {
+ return ops_.read_rollback_index(
+ ops, rollback_index_slot, out_rollback_index);
+ }
+
+ AvbIOResult write_rollback_index(AvbOps* ops,
+ size_t rollback_index_slot,
+ uint64_t rollback_index) override {
+ return ops_.write_rollback_index(ops, rollback_index_slot, rollback_index);
+ }
+
+ AvbIOResult read_is_device_unlocked(AvbOps* ops,
+ bool* out_is_device_unlocked) override {
+ return ops_.read_is_device_unlocked(ops, out_is_device_unlocked);
+ }
+
+ AvbIOResult get_unique_guid_for_partition(AvbOps* ops,
+ const char* partition,
+ char* guid_buf,
+ size_t guid_buf_size) override {
+ return ops_.get_unique_guid_for_partition(
+ ops, partition, guid_buf, guid_buf_size);
+ }
+
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override {
+ return ops_.read_permanent_attributes(attributes);
+ }
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override {
+ return ops_.read_permanent_attributes_hash(hash);
+ }
+
+ protected:
+ FakeAvbOps ops_;
+ AvbAtxPermanentAttributes attributes_;
+ int num_atx_calls_ = 0;
+
+ private:
+ void ReadAtxDefaultData() {
+ std::string tmp;
+ ASSERT_TRUE(
+ base::ReadFileToString(base::FilePath(kPermanentAttributesPath), &tmp));
+ ASSERT_EQ(tmp.size(), sizeof(AvbAtxPermanentAttributes));
+ memcpy(&attributes_, tmp.data(), tmp.size());
+ }
+};
+
+TEST_F(AvbAtxSlotVerifyTest, SlotVerifyWithAtx) {
+ std::string metadata_option = "--public_key_metadata=";
+ metadata_option += kMetadataPath;
+ GenerateVBMetaImage("vbmeta_a.img",
+ "SHA256_RSA2048",
+ 0,
+ base::FilePath("test/data/testkey_rsa2048.pem"),
+ metadata_option);
+
+ ops_.set_expected_public_key(
+ PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
+
+ AvbSlotVerifyData* slot_data = NULL;
+ const char* requested_partitions[] = {"boot", NULL};
+ EXPECT_EQ(AVB_SLOT_VERIFY_RESULT_OK,
+ avb_slot_verify(ops_.avb_ops(),
+ requested_partitions,
+ "_a",
+ false /* allow_verification_error */,
+ &slot_data));
+ EXPECT_NE(nullptr, slot_data);
+ avb_slot_verify_data_free(slot_data);
+ EXPECT_EQ(1, num_atx_calls_);
+}
+
+} // namespace avb
diff --git a/test/avbtool_unittest.cc b/test/avbtool_unittest.cc
index ee8a313..83cc881 100644
--- a/test/avbtool_unittest.cc
+++ b/test/avbtool_unittest.cc
@@ -1234,4 +1234,88 @@
vbmeta_path.value().c_str());
}
+TEST_F(AvbToolTest, MakeAtxPikCertificate) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa2048.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_certificate.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_certificate"
+ " --subject test/data/small_blob.bin"
+ " --subject_key %s"
+ " --subject_key_version 42"
+ " --subject_is_intermediate_authority"
+ " --authority_key test/data/testkey_rsa4096.pem"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_pik_certificate.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxPskCertificate) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa2048.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_certificate.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_certificate"
+ " --subject test/data/atx_product_id.bin"
+ " --subject_key %s"
+ " --subject_key_version 42"
+ " --authority_key test/data/testkey_rsa2048.pem"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_psk_certificate.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxPermanentAttributes) {
+ base::FilePath pubkey_path = testdir_.Append("tmp_pubkey.pem");
+ EXPECT_COMMAND(
+ 0,
+ "openssl pkey -pubout -in test/data/testkey_rsa4096.pem -out %s",
+ pubkey_path.value().c_str());
+
+ base::FilePath output_path = testdir_.Append("tmp_attributes.bin");
+ EXPECT_COMMAND(0,
+ "./avbtool make_atx_permanent_attributes"
+ " --root_authority_key %s"
+ " --product_id test/data/atx_product_id.bin"
+ " --output %s",
+ pubkey_path.value().c_str(),
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(0,
+ "diff test/data/atx_permanent_attributes.bin %s",
+ output_path.value().c_str());
+}
+
+TEST_F(AvbToolTest, MakeAtxMetadata) {
+ base::FilePath output_path = testdir_.Append("tmp_metadata.bin");
+
+ EXPECT_COMMAND(
+ 0,
+ "./avbtool make_atx_metadata"
+ " --intermediate_key_certificate test/data/atx_pik_certificate.bin"
+ " --product_key_certificate test/data/atx_psk_certificate.bin"
+ " --google_key_version 42"
+ " --output %s",
+ output_path.value().c_str());
+
+ EXPECT_COMMAND(
+ 0, "diff test/data/atx_metadata.bin %s", output_path.value().c_str());
+}
+
} // namespace avb
diff --git a/test/data/atx_metadata.bin b/test/data/atx_metadata.bin
new file mode 100644
index 0000000..3b53bbc
--- /dev/null
+++ b/test/data/atx_metadata.bin
Binary files differ
diff --git a/test/data/atx_permanent_attributes.bin b/test/data/atx_permanent_attributes.bin
new file mode 100644
index 0000000..be63c86
--- /dev/null
+++ b/test/data/atx_permanent_attributes.bin
Binary files differ
diff --git a/test/data/atx_pik_certificate.bin b/test/data/atx_pik_certificate.bin
new file mode 100644
index 0000000..d0b4a7b
--- /dev/null
+++ b/test/data/atx_pik_certificate.bin
Binary files differ
diff --git a/test/data/atx_product_id.bin b/test/data/atx_product_id.bin
new file mode 100644
index 0000000..d5648fa
--- /dev/null
+++ b/test/data/atx_product_id.bin
@@ -0,0 +1 @@
+t‹ùAö®÷ ìí=±]
\ No newline at end of file
diff --git a/test/data/atx_psk_certificate.bin b/test/data/atx_psk_certificate.bin
new file mode 100644
index 0000000..abec3ce
--- /dev/null
+++ b/test/data/atx_psk_certificate.bin
Binary files differ
diff --git a/test/data/testkey_rsa2048_public.pem b/test/data/testkey_rsa2048_public.pem
new file mode 100644
index 0000000..38b7f02
--- /dev/null
+++ b/test/data/testkey_rsa2048_public.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxlVR3TIkouAOvH79vaJT
+gFhpfvVKQIeVkFRZPVXK/zY0Gvrh4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni
++t/mtfjucxZfuLGC3kmJ1T3XqEKZgXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg
+0cghVhWZSCAndO8BenalC2v94/rtDfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2p
+fV1rWlLRdWmo33zeHBv52Rlbt0dMuXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC9
+1KcD0xuRCCM2WxH+r1lpszyIJDctYbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYm
+TQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/test/data/testkey_rsa4096_public.pem b/test/data/testkey_rsa4096_public.pem
new file mode 100644
index 0000000..efd7144
--- /dev/null
+++ b/test/data/testkey_rsa4096_public.pem
@@ -0,0 +1,14 @@
+-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ASv49OEbH4NiT3CjNMS
+VeliyfEPXswWcqtEfCxlSpS1FisAuwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3
+OhiuVKgV/rCtrDXaO60nvK/o0y83NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0
+Grjnx/r5CXerl5PrRK7PILzwgBHbIwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw
+7W6LvjBb9qav3YB8RV6PkZNeRP64ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWj
+XsrcVy8+8Mldhmr4r2an7c247aFfupuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH5
+9gJjKhot0RpmGxZBvb33TcBK5SdJX39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnY
+eUX/A0wmogBajsJRoRX5e/RcgZsYRzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6
+sklFL0fHDUE/l4BNP8G1u3BfpzevSCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3Bw
+Feq+xmwfYrP0LRaH+1YeRauuMuReke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfX
+DWxJx/XEkjGLCe4z2qk3tkkY+A5gRcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwA
+p13MfC7FlYujO/BDLl7dANsCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/test/fake_avb_ops.cc b/test/fake_avb_ops.cc
index b2a2080..cdd0850 100644
--- a/test/fake_avb_ops.cc
+++ b/test/fake_avb_ops.cc
@@ -37,6 +37,7 @@
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
+#include <openssl/sha.h>
#include "fake_avb_ops.h"
@@ -214,6 +215,26 @@
return AVB_IO_RESULT_OK;
}
+AvbIOResult FakeAvbOps::read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) {
+ *attributes = permanent_attributes_;
+ return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FakeAvbOps::read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ if (permanent_attributes_hash_.empty()) {
+ SHA256(reinterpret_cast<const unsigned char*>(&permanent_attributes_),
+ sizeof(AvbAtxPermanentAttributes),
+ hash);
+ return AVB_IO_RESULT_OK;
+ }
+ memset(hash, 0, AVB_SHA256_DIGEST_SIZE);
+ permanent_attributes_hash_.copy(reinterpret_cast<char*>(hash),
+ AVB_SHA256_DIGEST_SIZE);
+ return AVB_IO_RESULT_OK;
+}
+
static AvbIOResult my_ops_read_from_partition(AvbOps* ops,
const char* partition,
int64_t offset,
@@ -283,8 +304,23 @@
->get_unique_guid_for_partition(ops, partition, guid_buf, guid_buf_size);
}
+static AvbIOResult my_ops_read_permanent_attributes(
+ AvbAtxOps* atx_ops, AvbAtxPermanentAttributes* attributes) {
+ return FakeAvbOps::GetInstanceFromAvbOps(atx_ops->ops)
+ ->delegate()
+ ->read_permanent_attributes(attributes);
+}
+
+static AvbIOResult my_ops_read_permanent_attributes_hash(
+ AvbAtxOps* atx_ops, uint8_t hash[AVB_SHA256_DIGEST_SIZE]) {
+ return FakeAvbOps::GetInstanceFromAvbOps(atx_ops->ops)
+ ->delegate()
+ ->read_permanent_attributes_hash(hash);
+}
+
FakeAvbOps::FakeAvbOps() {
avb_ops_.ab_ops = &avb_ab_ops_;
+ avb_ops_.atx_ops = &avb_atx_ops_;
avb_ops_.user_data = this;
avb_ops_.read_from_partition = my_ops_read_from_partition;
avb_ops_.write_to_partition = my_ops_write_to_partition;
@@ -299,6 +335,11 @@
avb_ab_ops_.read_ab_metadata = avb_ab_data_read;
avb_ab_ops_.write_ab_metadata = avb_ab_data_write;
+ avb_atx_ops_.ops = &avb_ops_;
+ avb_atx_ops_.read_permanent_attributes = my_ops_read_permanent_attributes;
+ avb_atx_ops_.read_permanent_attributes_hash =
+ my_ops_read_permanent_attributes_hash;
+
delegate_ = this;
}
diff --git a/test/fake_avb_ops.h b/test/fake_avb_ops.h
index 90cbc95..0a7246d 100644
--- a/test/fake_avb_ops.h
+++ b/test/fake_avb_ops.h
@@ -30,6 +30,7 @@
#include <string>
#include <libavb_ab/libavb_ab.h>
+#include <libavb_atx/libavb_atx.h>
namespace avb {
@@ -72,6 +73,12 @@
const char* partition,
char* guid_buf,
size_t guid_buf_size) = 0;
+
+ virtual AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) = 0;
+
+ virtual AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) = 0;
};
// Provides fake implementations of AVB ops. All instances of this class must be
@@ -96,6 +103,10 @@
return &avb_ab_ops_;
}
+ AvbAtxOps* avb_atx_ops() {
+ return &avb_atx_ops_;
+ }
+
FakeAvbOpsDelegate* delegate() {
return delegate_;
}
@@ -131,6 +142,14 @@
stored_is_device_unlocked_ = stored_is_device_unlocked;
}
+ void set_permanent_attributes(const AvbAtxPermanentAttributes& attributes) {
+ permanent_attributes_ = attributes;
+ }
+
+ void set_permanent_attributes_hash(const std::string& hash) {
+ permanent_attributes_hash_ = hash;
+ }
+
// FakeAvbOpsDelegate methods.
AvbIOResult read_from_partition(const char* partition,
int64_t offset,
@@ -166,9 +185,16 @@
char* guid_buf,
size_t guid_buf_size) override;
+ AvbIOResult read_permanent_attributes(
+ AvbAtxPermanentAttributes* attributes) override;
+
+ AvbIOResult read_permanent_attributes_hash(
+ uint8_t hash[AVB_SHA256_DIGEST_SIZE]) override;
+
private:
AvbOps avb_ops_;
AvbABOps avb_ab_ops_;
+ AvbAtxOps avb_atx_ops_;
FakeAvbOpsDelegate* delegate_;
@@ -180,6 +206,9 @@
std::map<size_t, uint64_t> stored_rollback_indexes_;
bool stored_is_device_unlocked_;
+
+ AvbAtxPermanentAttributes permanent_attributes_;
+ std::string permanent_attributes_hash_;
};
} // namespace avb
diff --git a/test/user_code_test.cc b/test/user_code_test.cc
new file mode 100644
index 0000000..fd851f3
--- /dev/null
+++ b/test/user_code_test.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// This test ensures that user code can include libavb*.h and compile without
+// defining AVB_COMPILATION (which user code must not).
+#include "libavb/libavb.h"
+#include "libavb_ab/libavb_ab.h"
+#include "libavb_atx/libavb_atx.h"