// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. // This is an implementation of DiceGenerateCertificate and the crypto // operations that uses mbedtls. The algorithms used are SHA512, HKDF-SHA512, // and deterministic ECDSA-P256-SHA512. #include #include #include "dice/dice.h" #include "dice/ops.h" #include "dice/utils.h" #include "mbedtls/asn1.h" #include "mbedtls/asn1write.h" #include "mbedtls/bignum.h" #include "mbedtls/ecdsa.h" #include "mbedtls/ecp.h" #include "mbedtls/hkdf.h" #include "mbedtls/hmac_drbg.h" #include "mbedtls/md.h" #include "mbedtls/oid.h" #include "mbedtls/pk.h" #include "mbedtls/x509.h" #include "mbedtls/x509_crt.h" #define DICE_MAX_CERTIFICATE_SIZE 2048 #define DICE_MAX_EXTENSION_SIZE 2048 #define DICE_MAX_KEY_ID_SIZE 40 static DiceResult SetupKeyPair( const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], mbedtls_pk_context* context) { if (0 != mbedtls_pk_setup(context, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY))) { return kDiceResultPlatformError; } // Use the |private_key_seed| directly to seed a PRNG which is then in turn // used to generate the private key. This implementation uses HMAC_DRBG in a // loop with no reduction, like RFC6979. DiceResult result = kDiceResultOk; mbedtls_hmac_drbg_context rng_context; mbedtls_hmac_drbg_init(&rng_context); if (0 != mbedtls_hmac_drbg_seed_buf( &rng_context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), private_key_seed, DICE_PRIVATE_KEY_SEED_SIZE)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*context), mbedtls_hmac_drbg_random, &rng_context)) { result = kDiceResultPlatformError; goto out; } out: mbedtls_hmac_drbg_free(&rng_context); return result; } static DiceResult GetIdFromKey(void* context, const mbedtls_pk_context* pk_context, uint8_t id[DICE_ID_SIZE]) { uint8_t raw_public_key[33]; size_t raw_public_key_size = 0; mbedtls_ecp_keypair* key = mbedtls_pk_ec(*pk_context); if (0 != mbedtls_ecp_point_write_binary( &key->grp, &key->Q, MBEDTLS_ECP_PF_COMPRESSED, &raw_public_key_size, raw_public_key, sizeof(raw_public_key))) { return kDiceResultPlatformError; } return DiceDeriveCdiCertificateId(context, raw_public_key, raw_public_key_size, id); } // 54 byte name is prefix (13), hex id (40), and a null terminator. static void GetNameFromId(const uint8_t id[DICE_ID_SIZE], char name[54]) { strcpy(name, "serialNumber="); DiceHexEncode(id, /*num_bytes=*/DICE_ID_SIZE, (uint8_t*)&name[13], /*out_size=*/40); name[53] = '\0'; } static DiceResult GetSubjectKeyIdFromId(const uint8_t id[DICE_ID_SIZE], size_t buffer_size, uint8_t* buffer, size_t* actual_size) { uint8_t* pos = buffer + buffer_size; int length_or_error = mbedtls_asn1_write_octet_string(&pos, buffer, id, DICE_ID_SIZE); if (length_or_error < 0) { return kDiceResultPlatformError; } *actual_size = length_or_error; memmove(buffer, pos, *actual_size); return kDiceResultOk; } static int AddAuthorityKeyIdEncoding(uint8_t** pos, uint8_t* start, int length) { // From RFC 5280 4.2.1.1. const int kKeyIdentifierTag = 0; int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD. MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length)); MBEDTLS_ASN1_CHK_ADD( length, mbedtls_asn1_write_tag( pos, start, MBEDTLS_ASN1_CONTEXT_SPECIFIC | kKeyIdentifierTag)); MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length)); MBEDTLS_ASN1_CHK_ADD( length, mbedtls_asn1_write_tag(pos, start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return length; } static DiceResult GetAuthorityKeyIdFromId(const uint8_t id[DICE_ID_SIZE], size_t buffer_size, uint8_t* buffer, size_t* actual_size) { uint8_t* pos = buffer + buffer_size; int length_or_error = mbedtls_asn1_write_raw_buffer(&pos, buffer, id, DICE_ID_SIZE); if (length_or_error < 0) { return kDiceResultPlatformError; } length_or_error = AddAuthorityKeyIdEncoding(&pos, buffer, length_or_error); if (length_or_error < 0) { return kDiceResultPlatformError; } *actual_size = length_or_error; memmove(buffer, pos, *actual_size); return kDiceResultOk; } static uint8_t GetFieldTag(uint8_t tag) { return MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | tag; } // Can be used with MBEDTLS_ASN1_CHK_ADD. static int WriteExplicitModeField(uint8_t tag, int value, uint8_t** pos, uint8_t* start) { // ASN.1 constants not defined by mbedtls. const uint8_t kEnumTypeTag = 10; int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD. int field_length = 0; MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_int(pos, start, value)); // Overwrite the 'int' type. ++(*pos); --field_length; MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_tag(pos, start, kEnumTypeTag)); // Explicitly tagged, so add the field tag too. MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_len(pos, start, field_length)); MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag))); return field_length; } // Can be used with MBEDTLS_ASN1_CHK_ADD. static int WriteExplicitUtf8StringField(uint8_t tag, const void* value, size_t value_size, uint8_t** pos, uint8_t* start) { int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD. int field_length = 0; MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_utf8_string( pos, start, value, value_size)); // Explicitly tagged, so add the field tag too. MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_len(pos, start, field_length)); MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag))); return field_length; } // Can be used with MBEDTLS_ASN1_CHK_ADD. static int WriteExplicitOctetStringField(uint8_t tag, const uint8_t* value, size_t value_size, uint8_t** pos, uint8_t* start) { int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD. int field_length = 0; MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_octet_string( pos, start, value, value_size)); // Explicitly tagged, so add the field tag too. MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_len(pos, start, field_length)); MBEDTLS_ASN1_CHK_ADD(field_length, mbedtls_asn1_write_tag(pos, start, GetFieldTag(tag))); return field_length; } static int GetDiceExtensionDataHelper(const DiceInputValues* input_values, uint8_t** pos, uint8_t* start) { // ASN.1 tags for extension fields. const uint8_t kDiceFieldCodeHash = 0; const uint8_t kDiceFieldCodeDescriptor = 1; const uint8_t kDiceFieldConfigHash = 2; const uint8_t kDiceFieldConfigDescriptor = 3; const uint8_t kDiceFieldAuthorityHash = 4; const uint8_t kDiceFieldAuthorityDescriptor = 5; const uint8_t kDiceFieldMode = 6; const uint8_t kDiceFieldProfileName = 7; // Build up the extension ASN.1 in reverse order. int ret = 0; // Used by MBEDTLS_ASN1_CHK_ADD. int length = 0; // Add the profile name field. if (DICE_PROFILE_NAME) { MBEDTLS_ASN1_CHK_ADD(length, WriteExplicitUtf8StringField( kDiceFieldProfileName, DICE_PROFILE_NAME, strlen(DICE_PROFILE_NAME), pos, start)); } // Add the mode field. MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitModeField(kDiceFieldMode, input_values->mode, pos, start)); // Add the authorityDescriptor field, if applicable. if (input_values->authority_descriptor_size > 0) { MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField( kDiceFieldAuthorityDescriptor, input_values->authority_descriptor, input_values->authority_descriptor_size, pos, start)); } // Add the authorityHash field. MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField(kDiceFieldAuthorityHash, input_values->authority_hash, DICE_HASH_SIZE, pos, start)); // Add the configurationDescriptor field (and configurationHash field, if // applicable). if (input_values->config_type == kDiceConfigTypeDescriptor) { uint8_t hash[DICE_HASH_SIZE]; int result = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), input_values->config_descriptor, input_values->config_descriptor_size, hash); if (result) { return result; } MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField( kDiceFieldConfigDescriptor, input_values->config_descriptor, input_values->config_descriptor_size, pos, start)); MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField(kDiceFieldConfigHash, hash, DICE_HASH_SIZE, pos, start)); } else if (input_values->config_type == kDiceConfigTypeInline) { MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField( kDiceFieldConfigDescriptor, input_values->config_value, DICE_INLINE_CONFIG_SIZE, pos, start)); } // Add the code descriptor field, if applicable. if (input_values->code_descriptor_size > 0) { MBEDTLS_ASN1_CHK_ADD( length, WriteExplicitOctetStringField( kDiceFieldCodeDescriptor, input_values->code_descriptor, input_values->code_descriptor_size, pos, start)); } // Add the code hash field. MBEDTLS_ASN1_CHK_ADD(length, WriteExplicitOctetStringField( kDiceFieldCodeHash, input_values->code_hash, DICE_HASH_SIZE, pos, start)); // Add the sequence length and tag. MBEDTLS_ASN1_CHK_ADD(length, mbedtls_asn1_write_len(pos, start, length)); MBEDTLS_ASN1_CHK_ADD( length, mbedtls_asn1_write_tag(pos, start, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); return length; } static DiceResult GetDiceExtensionData(const DiceInputValues* input_values, size_t buffer_size, uint8_t* buffer, size_t* actual_size) { uint8_t* pos = buffer + buffer_size; int length_or_error = GetDiceExtensionDataHelper(input_values, &pos, buffer); if (length_or_error == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL) { return kDiceResultBufferTooSmall; } else if (length_or_error < 0) { return kDiceResultPlatformError; } *actual_size = length_or_error; memmove(buffer, pos, *actual_size); return kDiceResultOk; } DiceResult DiceHash(void* context_not_used, const uint8_t* input, size_t input_size, uint8_t output[DICE_HASH_SIZE]) { (void)context_not_used; if (0 != mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), input, input_size, output)) { return kDiceResultPlatformError; } return kDiceResultOk; } DiceResult DiceKdf(void* context_not_used, size_t length, const uint8_t* ikm, size_t ikm_size, const uint8_t* salt, size_t salt_size, const uint8_t* info, size_t info_size, uint8_t* output) { (void)context_not_used; if (0 != mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), salt, salt_size, ikm, ikm_size, info, info_size, output, length)) { return kDiceResultPlatformError; } return kDiceResultOk; } DiceResult DiceGenerateCertificate( void* context, const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], const DiceInputValues* input_values, size_t certificate_buffer_size, uint8_t* certificate, size_t* certificate_actual_size) { // 1.3.6.1.4.1.11129.2.1.24 // iso.org.dod.internet.private.enterprise. // google.googleSecurity.certificateExtensions.diceAttestationData const char* kDiceExtensionOid = MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_DOD "\x01\x04\x01\xd6\x79\x02\x01\x18"; const size_t kDiceExtensionOidLength = 10; DiceResult result = kDiceResultOk; // Initialize variables cleaned up on 'goto out'. mbedtls_pk_context authority_key_context; mbedtls_pk_init(&authority_key_context); mbedtls_pk_context subject_key_context; mbedtls_pk_init(&subject_key_context); mbedtls_x509write_cert cert_context; mbedtls_x509write_crt_init(&cert_context); mbedtls_mpi serial_number; mbedtls_mpi_init(&serial_number); // Derive key pairs and IDs. result = SetupKeyPair(authority_private_key_seed, &authority_key_context); if (result != kDiceResultOk) { goto out; } uint8_t authority_id[DICE_ID_SIZE]; result = GetIdFromKey(context, &authority_key_context, authority_id); if (result != kDiceResultOk) { goto out; } char authority_name[54]; GetNameFromId(authority_id, authority_name); uint8_t authority_key_id[DICE_MAX_KEY_ID_SIZE]; size_t authority_key_id_size = 0; result = GetAuthorityKeyIdFromId(authority_id, sizeof(authority_key_id), authority_key_id, &authority_key_id_size); if (result != kDiceResultOk) { goto out; } result = SetupKeyPair(subject_private_key_seed, &subject_key_context); if (result != kDiceResultOk) { goto out; } uint8_t subject_id[DICE_ID_SIZE]; result = GetIdFromKey(context, &subject_key_context, subject_id); if (result != kDiceResultOk) { goto out; } char subject_name[54]; GetNameFromId(subject_id, subject_name); uint8_t subject_key_id[DICE_MAX_KEY_ID_SIZE]; size_t subject_key_id_size = 0; result = GetSubjectKeyIdFromId(subject_id, sizeof(subject_key_id), subject_key_id, &subject_key_id_size); if (result != kDiceResultOk) { goto out; } uint8_t dice_extension[DICE_MAX_EXTENSION_SIZE]; size_t dice_extension_size = 0; result = GetDiceExtensionData(input_values, sizeof(dice_extension), dice_extension, &dice_extension_size); if (result != kDiceResultOk) { goto out; } // Construct the certificate. mbedtls_x509write_crt_set_version(&cert_context, MBEDTLS_X509_CRT_VERSION_3); if (0 != mbedtls_mpi_read_binary(&serial_number, subject_id, sizeof(subject_id))) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_serial(&cert_context, &serial_number)) { result = kDiceResultPlatformError; goto out; } // '20180322235959' is the date of publication of the DICE specification. Here // it's used as a somewhat arbitrary backstop. '99991231235959' is suggested // by RFC 5280 in cases where expiry is not meaningful. Basically, the // certificate never expires. if (0 != mbedtls_x509write_crt_set_validity(&cert_context, "20180322235959", "99991231235959")) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_issuer_name(&cert_context, authority_name)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_subject_name(&cert_context, subject_name)) { result = kDiceResultPlatformError; goto out; } mbedtls_x509write_crt_set_subject_key(&cert_context, &subject_key_context); mbedtls_x509write_crt_set_issuer_key(&cert_context, &authority_key_context); mbedtls_x509write_crt_set_md_alg(&cert_context, MBEDTLS_MD_SHA512); if (0 != mbedtls_x509write_crt_set_extension( &cert_context, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER, MBEDTLS_OID_SIZE(MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER), /*critical=*/0, authority_key_id, authority_key_id_size)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_extension( &cert_context, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER, MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER), /*critical=*/0, subject_key_id, subject_key_id_size)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_key_usage(&cert_context, MBEDTLS_X509_KU_KEY_CERT_SIGN)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_basic_constraints(&cert_context, /*is_ca=*/1, /*max_pathlen=*/-1)) { result = kDiceResultPlatformError; goto out; } if (0 != mbedtls_x509write_crt_set_extension( &cert_context, kDiceExtensionOid, kDiceExtensionOidLength, /*critical=*/1, dice_extension, dice_extension_size)) { result = kDiceResultPlatformError; goto out; } // This implementation is deterministic and assumes entropy is not available. // If this code is run where entropy is available, however, f_rng and p_rng // should be set to use that entropy. As is, we'll provide a DRBG for blinding // but it will be ineffective. mbedtls_hmac_drbg_context drbg; mbedtls_hmac_drbg_init(&drbg); mbedtls_hmac_drbg_seed_buf(&drbg, mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), subject_key_id, subject_key_id_size); uint8_t tmp_buffer[DICE_MAX_CERTIFICATE_SIZE]; int length_or_error = mbedtls_x509write_crt_der(&cert_context, tmp_buffer, sizeof(tmp_buffer), mbedtls_hmac_drbg_random, &drbg); mbedtls_hmac_drbg_free(&drbg); if (length_or_error < 0) { result = kDiceResultPlatformError; goto out; } *certificate_actual_size = length_or_error; if (*certificate_actual_size > certificate_buffer_size) { result = kDiceResultBufferTooSmall; goto out; } // The certificate has been written to the end of tmp_buffer. Skip unused // buffer when copying. memcpy(certificate, &tmp_buffer[sizeof(tmp_buffer) - *certificate_actual_size], *certificate_actual_size); out: mbedtls_mpi_free(&serial_number); mbedtls_x509write_crt_free(&cert_context); mbedtls_pk_free(&authority_key_context); mbedtls_pk_free(&subject_key_context); return result; }