blob: b3d5e5749e89eb9e47f604a009fca10b7712dba8 [file] [log] [blame]
// 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 <stdint.h>
#include <string.h>
#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;
}