1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-30 17:21:21 +00:00

feat(core/prodtest): Check device certificate chain integrity using authority key identifier.

[no changelog]
This commit is contained in:
Andrew Kozlik 2023-12-08 15:45:12 +01:00 committed by matejcik
parent a1be6f8aef
commit 8de2d8989d
2 changed files with 119 additions and 26 deletions

View File

@ -51,6 +51,18 @@ static const optiga_metadata_item KEY_USE_SIGN = {
static const optiga_metadata_item TYPE_PTFBIND = { static const optiga_metadata_item TYPE_PTFBIND = {
(const uint8_t[]){OPTIGA_DATA_TYPE_PTFBIND}, 1}; (const uint8_t[]){OPTIGA_DATA_TYPE_PTFBIND}, 1};
// Identifier of context-specific constructed tag 3, which is used for
// extensions in X.509.
#define DER_X509_EXTENSIONS 0xa3
// Identifier of context-specific primitive tag 0, which is used for
// keyIdentifier in authorityKeyIdentifier.
#define DER_X509_KEY_IDENTIFIER 0x80
// DER-encoded object identifier of the authority key identifier extension
// (id-ce-authorityKeyIdentifier).
const uint8_t OID_AUTHORITY_KEY_IDENTIFIER[] = {0x06, 0x03, 0x55, 0x1d, 0x23};
static bool optiga_paired(void) { static bool optiga_paired(void) {
const char *details = ""; const char *details = "";
@ -540,26 +552,90 @@ static const uint8_t ECDSA_WITH_SHA256[] = {
}; };
// clang-format on // clang-format on
static const uint8_t ROOT_PUBLIC_KEYS[][65] = { static bool get_cert_extensions(DER_ITEM *tbs_cert, DER_ITEM *extensions) {
{ // Find the certificate extensions in the tbsCertificate.
// Production root public key. DER_ITEM cert_item = {0};
0x04, 0xca, 0x97, 0x48, 0x0a, 0xc0, 0xd7, 0xb1, 0xe6, 0xef, 0xaf, while (der_read_item(&tbs_cert->buf, &cert_item)) {
0xe5, 0x18, 0xcd, 0x43, 0x3c, 0xec, 0x2b, 0xf8, 0xab, 0x98, 0x22, if (cert_item.id == DER_X509_EXTENSIONS) {
0xd7, 0x6e, 0xaf, 0xd3, 0x43, 0x63, 0xb5, 0x5d, 0x63, 0xe6, 0x03, // Open the extensions sequence.
0x80, 0xbf, 0xf2, 0x0a, 0xcc, 0x75, 0xcd, 0xe0, 0x3c, 0xff, 0xcb, return der_read_item(&cert_item.buf, extensions) &&
0x50, 0xab, 0x6f, 0x8c, 0xe7, 0x0c, 0x87, 0x8e, 0x37, 0xeb, 0xc5, extensions->id == DER_SEQUENCE;
0x8f, 0xf7, 0xcc, 0xa0, 0xa8, 0x3b, 0x16, 0xb1, 0x5f, 0xa5, }
}, }
{ return false;
// Development root public key. }
0x04, 0x7f, 0x77, 0x36, 0x8d, 0xea, 0x2d, 0x4d, 0x61, 0xe9, 0x89,
0xf4, 0x74, 0xa5, 0x67, 0x23, 0xc3, 0x21, 0x2d, 0xac, 0xf8, 0xa8, static bool get_extension_value(const uint8_t *extension_oid,
0x08, 0xd8, 0x79, 0x55, 0x95, 0xef, 0x38, 0x44, 0x14, 0x27, 0xc4, size_t extension_oid_size, DER_ITEM *extensions,
0x38, 0x9b, 0xc4, 0x54, 0xf0, 0x20, 0x89, 0xd7, 0xf0, 0x8b, 0x87, DER_ITEM *extension_value) {
0x30, 0x05, 0xe4, 0xc2, 0x8d, 0x43, 0x24, 0x68, 0x99, 0x78, 0x71, // Find the extension with the given OID.
0xc0, 0xbf, 0x28, 0x6f, 0xd3, 0x86, 0x1e, 0x21, 0xe9, 0x6a, DER_ITEM extension = {0};
}, while (der_read_item(&extensions->buf, &extension)) {
}; DER_ITEM extension_id = {0};
if (der_read_item(&extension.buf, &extension_id) &&
extension_id.buf.size == extension_oid_size &&
memcmp(extension_id.buf.data, extension_oid, extension_oid_size) == 0) {
// Find the extension's extnValue, skipping the optional critical flag.
while (der_read_item(&extension.buf, extension_value)) {
if (extension_value->id == DER_OCTET_STRING) {
return true;
}
}
memzero(extension_value, sizeof(DER_ITEM));
return false;
}
}
return false;
}
static bool get_authority_key_digest(DER_ITEM *tbs_cert,
const uint8_t **authority_key_digest) {
DER_ITEM extensions = {0};
if (!get_cert_extensions(tbs_cert, &extensions)) {
vcp_println("ERROR get_authority_key_digest, extensions not found.");
return false;
}
// Find the authority key identifier extension's extnValue.
DER_ITEM extension_value = {0};
if (!get_extension_value(OID_AUTHORITY_KEY_IDENTIFIER,
sizeof(OID_AUTHORITY_KEY_IDENTIFIER), &extensions,
&extension_value)) {
vcp_println(
"ERROR get_authority_key_digest, authority key identifier extension "
"not found.");
return false;
}
// Open the AuthorityKeyIdentifier sequence.
DER_ITEM auth_key_id = {0};
if (!der_read_item(&extension_value.buf, &auth_key_id) ||
auth_key_id.id != DER_SEQUENCE) {
vcp_println(
"ERROR get_authority_key_digest, failed to open authority key "
"identifier extnValue.");
return false;
}
// Find the keyIdentifier field.
DER_ITEM key_id = {0};
if (!der_read_item(&auth_key_id.buf, &key_id) ||
key_id.id != DER_X509_KEY_IDENTIFIER) {
vcp_println(
"ERROR get_authority_key_digest, failed to find keyIdentifier field.");
return false;
}
// Return the pointer to the keyIdentifier data.
if (buffer_remaining(&key_id.buf) != SHA1_DIGEST_LENGTH ||
!buffer_ptr(&key_id.buf, authority_key_digest)) {
vcp_println(
"ERROR get_authority_key_digest, invalid length of keyIdentifier.");
return false;
}
return true;
}
bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) { bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
// Checks the integrity of the device certificate chain to ensure that the // Checks the integrity of the device certificate chain to ensure that the
@ -593,6 +669,10 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
return false; return false;
} }
// This will be populated with a pointer to the key identifier data of the
// AuthorityKeyIdentifier extension from the last certificate in the chain.
const uint8_t *authority_key_digest = NULL;
BUFFER_READER chain_reader = {0}; BUFFER_READER chain_reader = {0};
buffer_reader_init(&chain_reader, chain, chain_size); buffer_reader_init(&chain_reader, chain, chain_size);
int cert_count = 0; int cert_count = 0;
@ -652,6 +732,13 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
return false; return false;
} }
// Get the authority key identifier from the last certificate.
if (buffer_remaining(&chain_reader) == 0 &&
!get_authority_key_digest(&tbs_cert, &authority_key_digest)) {
// Error returned by get_authority_key_digest().
return false;
}
// Prepare the hash of tbsCertificate for the next signature verification. // Prepare the hash of tbsCertificate for the next signature verification.
sha256_Raw(tbs_cert.buf.data, tbs_cert.buf.size, digest); sha256_Raw(tbs_cert.buf.data, tbs_cert.buf.size, digest);
@ -689,15 +776,20 @@ bool check_device_cert_chain(const uint8_t *chain, size_t chain_size) {
} }
} }
// Verify that the last certificate in the chain is valid for one of the known // Verify that the signature of the last certificate in the chain matches its
// root public keys. // own AuthorityKeyIdentifier to verify the integrity of the certificate data.
for (int i = 0; i < sizeof(ROOT_PUBLIC_KEYS) / sizeof(ROOT_PUBLIC_KEYS[0]); uint8_t pub_key[65] = {0};
++i) { uint8_t pub_key_digest[SHA1_DIGEST_LENGTH] = {0};
if (ecdsa_verify_digest(&nist256p1, ROOT_PUBLIC_KEYS[i], sig, digest) == for (int recid = 0; recid < 4; ++recid) {
if (ecdsa_recover_pub_from_sig(&nist256p1, pub_key, sig, digest, recid) ==
0) { 0) {
sha1_Raw(pub_key, sizeof(pub_key), pub_key_digest);
if (memcmp(authority_key_digest, pub_key_digest,
sizeof(pub_key_digest)) == 0) {
return true; return true;
} }
} }
}
vcp_println("ERROR check_device_cert_chain, ecdsa_verify_digest root."); vcp_println("ERROR check_device_cert_chain, ecdsa_verify_digest root.");
return false; return false;

View File

@ -33,6 +33,7 @@
#define DER_SEQUENCE 0x30 #define DER_SEQUENCE 0x30
#define DER_INTEGER 0x02 #define DER_INTEGER 0x02
#define DER_BIT_STRING 0x03 #define DER_BIT_STRING 0x03
#define DER_OCTET_STRING 0x04
// Struct representing a DER-encoded ASN.1 data value. // Struct representing a DER-encoded ASN.1 data value.
typedef struct { typedef struct {