Files
oemcrypto/oemcrypto/opk/oemcrypto_ta/wtpi_reference/cose_util.c
Googler b47d0c2db0 OEMCrypto and OPK 19.6.0
GitOrigin-RevId: 13a33e34413c19da1bfe76abcc66be519c9ac9d1
2025-06-09 23:56:25 -07:00

758 lines
28 KiB
C

/* Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
source code may only be used and distributed under the Widevine License
Agreement. */
#include "cose_util.h"
#include <stdio.h>
#include <string.h>
#include "device_info_util.h"
#include "dice/cbor_reader.h"
#include "dice/cbor_writer.h"
#include "dice/config.h"
#include "dice/dice.h"
#include "oemcrypto_api_macros.h"
#include "oemcrypto_check_macros.h"
#include "wtpi_crypto_and_key_management_interface_layer1.h"
#include "wtpi_crypto_asymmetric_interface.h"
/******************************************************************************
The following are copied from open-dice library cbor_cert_op.c
*******************************************************************************/
#define DICE_PUBLIC_KEY_SIZE_ED25519 32
#define DICE_SIGNATURE_SIZE_ED25519 64
#define DICE_SIGNATURE_SIZE_P256 64
#define DICE_PUBLIC_KEY_SIZE_P256 64
// Max size of the COSE_Sign1 protected attributes.
#define DICE_MAX_PROTECTED_ATTRIBUTES_SIZE 16
#define DICE_MAX_CONFIGURATION_DESCRIPTOR_SIZE 64
static DiceResult DiceCoseEncodePublicKeyEd25519(const uint8_t* public_key,
size_t public_key_size,
size_t buffer_size,
uint8_t* buffer,
size_t* encoded_size) {
ABORT_IF_NULL(public_key);
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
if (public_key_size != DICE_PUBLIC_KEY_SIZE_ED25519) {
return kDiceResultInvalidInput;
}
// Constants per RFC 8152.
const int64_t kCoseKeyKtyLabel = 1;
const int64_t kCoseKeyAlgLabel = 3;
const int64_t kCoseKeyOpsLabel = 4;
const int64_t kCoseOkpCrvLabel = -1;
const int64_t kCoseOkpXLabel = -2;
const int64_t kCoseKeyTypeOkp = 1;
const int64_t kCoseAlgEdDSA = -8;
const int64_t kCoseKeyOpsVerify = 2;
const int64_t kCoseCrvEd25519 = 6;
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
CborWriteMap(/*num_pairs=*/5, &out);
// Add the key type.
CborWriteInt(kCoseKeyKtyLabel, &out);
CborWriteInt(kCoseKeyTypeOkp, &out);
// Add the algorithm.
CborWriteInt(kCoseKeyAlgLabel, &out);
CborWriteInt(kCoseAlgEdDSA, &out);
// Add the KeyOps.
CborWriteInt(kCoseKeyOpsLabel, &out);
CborWriteArray(/*num_elements=*/1, &out);
CborWriteInt(kCoseKeyOpsVerify, &out);
// Add the curve.
CborWriteInt(kCoseOkpCrvLabel, &out);
CborWriteInt(kCoseCrvEd25519, &out);
// Add the public key.
CborWriteInt(kCoseOkpXLabel, &out);
CborWriteBstr(/*data_size=*/public_key_size, public_key, &out);
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
static DiceResult DiceCoseEncodePublicKeyECDSA(const uint8_t* public_key,
size_t public_key_size,
size_t buffer_size,
uint8_t* buffer,
size_t* encoded_size) {
ABORT_IF_NULL(public_key);
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
if (public_key_size != DICE_PUBLIC_KEY_SIZE_P256) {
return kDiceResultInvalidInput;
}
// Constants per RFC 8152.
const int64_t kCoseKeyKtyLabel = 1;
const int64_t kCoseKeyAlgLabel = 3;
const int64_t kCoseKeyOpsLabel = 4;
const int64_t kCoseEc2CrvLabel = -1;
const int64_t kCoseEc2XLabel = -2;
const int64_t kCoseEc2YLabel = -3;
const int64_t kCoseKeyTypeEc2 = 2;
const int64_t kCoseAlgES256 = -7;
const int64_t kCoseKeyOpsVerify = 2;
const int64_t kCoseCrvP256 = 1;
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
CborWriteMap(/*num_pairs=*/6, &out);
// Add the key type.
CborWriteInt(kCoseKeyKtyLabel, &out);
CborWriteInt(kCoseKeyTypeEc2, &out);
// Add the algorithm.
CborWriteInt(kCoseKeyAlgLabel, &out);
CborWriteInt(kCoseAlgES256, &out);
// Add the KeyOps.
CborWriteInt(kCoseKeyOpsLabel, &out);
CborWriteArray(/*num_elements=*/1, &out);
CborWriteInt(kCoseKeyOpsVerify, &out);
// Add the curve.
CborWriteInt(kCoseEc2CrvLabel, &out);
CborWriteInt(kCoseCrvP256, &out);
// Add the public key.
CborWriteInt(kCoseEc2XLabel, &out);
CborWriteBstr(/*data_size=*/public_key_size / 2, public_key, &out);
CborWriteInt(kCoseEc2YLabel, &out);
CborWriteBstr(/*data_size=*/public_key_size / 2,
public_key + (public_key_size / 2), &out);
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
static DiceResult DiceCoseEncodePublicKey(const uint8_t* public_key,
size_t public_key_size,
size_t buffer_size, uint8_t* buffer,
size_t* encoded_size,
AsymmetricKeyType key_type) {
ABORT_IF_NULL(public_key);
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
switch (key_type) {
case PROV40_ED25519_PRIVATE_KEY:
return DiceCoseEncodePublicKeyEd25519(public_key, public_key_size,
buffer_size, buffer, encoded_size);
case DRM_ECC_PRIVATE_KEY:
return DiceCoseEncodePublicKeyECDSA(public_key, public_key_size,
buffer_size, buffer, encoded_size);
default:
return kDiceResultInvalidInput;
}
return kDiceResultInvalidInput;
}
static DiceResult EncodeProtectedAttributes(size_t buffer_size, uint8_t* buffer,
size_t* encoded_size,
AsymmetricKeyType key_type) {
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
// Constants per RFC 8152.
const int64_t kCoseHeaderAlgLabel = 1;
const int64_t kCoseAlgEdDSA = -8;
const int64_t kCoseAlgES256 = -7;
int64_t coseAlg = kCoseAlgEdDSA;
switch (key_type) {
case PROV40_ED25519_PRIVATE_KEY:
coseAlg = kCoseAlgEdDSA;
break;
case DRM_ECC_PRIVATE_KEY:
coseAlg = kCoseAlgES256;
break;
default:
return kDiceResultInvalidInput;
}
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
CborWriteMap(/*num_pairs=*/1, &out);
// Add the algorithm.
CborWriteInt(kCoseHeaderAlgLabel, &out);
CborWriteInt(coseAlg, &out);
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
static DiceResult EncodeConfigurationDescriptor(size_t buffer_size,
uint8_t* buffer,
size_t* encoded_size,
uint32_t entry_index,
bool is_leaf) {
/*
-4670548 : bstr .cbor { ; Configuration Descriptor
? -70002 : tstr, ; Component name
? -70003 : int, ; Component version
? -70004 : null, ; Resettable
? -70005 : uint, ; Security version
? -70006 : null, ; RKP VM marker
}
*/
ABORT_IF_ZERO(buffer_size);
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
const int64_t kComponentNameLabel = -70002;
const int64_t kComponentVersionLabel = -70003;
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
CborWriteMap(/*num_pairs=*/2, &out);
char str_buf[32] = {0};
// Add component name.
CborWriteInt(kComponentNameLabel, &out);
if (is_leaf) {
// The leaf certificate's component name must contain "Widevine".
CborWriteTstr("Widevine", &out);
} else {
snprintf(str_buf, sizeof(str_buf), "Component %u", entry_index);
CborWriteTstr(str_buf, &out);
memset(str_buf, 0, sizeof(str_buf));
}
// Add component version.
CborWriteInt(kComponentVersionLabel, &out);
snprintf(str_buf, sizeof(str_buf), "%d", API_MAJOR_VERSION);
CborWriteTstr(str_buf, &out);
memset(str_buf, 0, sizeof(str_buf));
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
static DiceResult EncodeCoseTbs(const uint8_t* protected_attributes,
size_t protected_attributes_size,
const uint8_t* payload, size_t payload_size,
const uint8_t* aad, size_t aad_size,
size_t buffer_size, uint8_t* buffer,
size_t* encoded_size) {
ABORT_IF_NULL(protected_attributes);
ABORT_IF_NULL(payload);
ABORT_IF_NULL(buffer);
ABORT_IF_NULL(encoded_size);
// aad is specifically allowed to be null
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
// TBS is an array of four elements.
CborWriteArray(/*num_elements=*/4, &out);
// Context string field.
CborWriteTstr("Signature1", &out);
// Protected attributes from COSE_Sign1.
CborWriteBstr(protected_attributes_size, protected_attributes, &out);
// Additional authenticated data.
CborWriteBstr(aad_size, aad, &out);
// Payload from COSE_Sign1.
CborWriteBstr(payload_size, payload, &out);
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
static DiceResult EncodeCoseSign1(const uint8_t* protected_attributes,
size_t protected_attributes_size,
const uint8_t* payload, size_t payload_size,
const uint8_t* signature,
size_t signature_size, size_t buffer_size,
uint8_t* buffer, size_t* encoded_size) {
if (signature_size != DICE_SIGNATURE_SIZE_ED25519 ||
signature_size != DICE_SIGNATURE_SIZE_P256) {
return kDiceResultInvalidInput;
}
struct CborOut out;
CborOutInit(buffer, buffer_size, &out);
// COSE_Sign1 is an array of four elements.
CborWriteArray(/*num_elements=*/4, &out);
// Protected attributes.
CborWriteBstr(protected_attributes_size, protected_attributes, &out);
// Empty map for unprotected attributes.
CborWriteMap(/*num_pairs=*/0, &out);
// Payload.
CborWriteBstr(payload_size, payload, &out);
// Signature.
CborWriteBstr(/*data_size=*/signature_size, signature, &out);
if (CborOutOverflowed(&out)) {
return kDiceResultBufferTooSmall;
}
*encoded_size = CborOutSize(&out);
return kDiceResultOk;
}
OEMCryptoResult DiceCoseSignAndEncodeSign1(const uint8_t* payload,
size_t payload_size,
AsymmetricKeyType key_type,
WTPI_AsymmetricKey_Handle key,
size_t buffer_size, uint8_t* buffer,
size_t* encoded_size) {
RETURN_INVALID_CONTEXT_IF_NULL(payload);
RETURN_INVALID_CONTEXT_IF_ZERO(payload_size);
RETURN_INVALID_CONTEXT_IF_NULL(key);
RETURN_INVALID_CONTEXT_IF_ZERO(buffer_size);
RETURN_INVALID_CONTEXT_IF_NULL(buffer);
RETURN_INVALID_CONTEXT_IF_NULL(encoded_size);
*encoded_size = 0;
// The encoded protected attributes are used in the TBS and the final
// COSE_Sign1 structure.
uint8_t protected_attributes[DICE_MAX_PROTECTED_ATTRIBUTES_SIZE];
size_t protected_attributes_size = 0;
DiceResult dice_result = EncodeProtectedAttributes(
sizeof(protected_attributes), protected_attributes,
&protected_attributes_size, key_type);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Construct a To-Be-Signed (TBS) structure based on the relevant fields of
// the COSE_Sign1.
dice_result = EncodeCoseTbs(
protected_attributes, protected_attributes_size, payload, payload_size,
/*aad=*/NULL, /*aad_size=*/0, buffer_size, buffer, encoded_size);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// Sign the TBS with the authority key.
uint8_t signature[DICE_SIGNATURE_SIZE_P256]; // P256 is largest supported so
size_t signature_length = sizeof(signature);
OEMCryptoResult result = CoseSignOp(key, key_type, buffer, *encoded_size,
signature, &signature_length);
if (result != OEMCrypto_SUCCESS) {
return result;
}
// The final certificate is an untagged COSE_Sign1 structure.
dice_result = EncodeCoseSign1(
protected_attributes, protected_attributes_size, payload, payload_size,
signature, signature_length, buffer_size, buffer, encoded_size);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
return OEMCrypto_SUCCESS;
}
/******************************************************************************
The following are added by Widevine
*******************************************************************************/
// The minimum buffer length required to hold the generated BCC.
#define BCC_TOTAL_LENGTH_ED25519 2048
#define BCC_TOTAL_LENGTH_P256 254
#define BCC_MAX_PAYLOAD_LENGTH 512
#if BCC_TOTAL_LENGTH_ED25519 >= BCC_TOTAL_LENGTH_P256
# define BCC_MAX_LENGTH BCC_TOTAL_LENGTH_ED25519
#else
# define BCC_MAX_LENGTH BCC_TOTAL_LENGTH_P256
#endif
// CSR is defined in:
// https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
#define CSR_PAYLOAD_MAX_LENGTH 2048
static OEMCryptoResult GenerateEncodedBccPayload(
const uint8_t* encoded_public_key, size_t encoded_public_key_size,
uint32_t entry_index, bool is_leaf, uint8_t* out, size_t* out_length) {
ABORT_IF_NULL(encoded_public_key);
ABORT_IF_ZERO(encoded_public_key_size);
ABORT_IF_NULL(out);
ABORT_IF_NULL(out_length);
ABORT_IF_ZERO(*out_length);
const int64_t kIssuerLabel = 1;
const int64_t kSubjectLabel = 2;
const int64_t kProfileNameLabel = -4670554;
const int64_t kSubjectPublicKeyLabel = -4670552;
const int64_t kKeyUsageLabel = -4670553;
const uint8_t kKeyUsageCertSign = 32;
const int64_t kCodeHashLabel = -4670545;
const int64_t kConfigurationHashLabel = -4670547;
const int64_t kConfigurationDescriptorLabel = -4670548;
const int64_t kAuthorityHashLabel = -4670549;
const int64_t kModeLabel = -4670551;
// Mode values 0: Not Configured, 1: Normal, 2: Debug, 3: Recovery
const uint8_t mode = 1;
char str_buf[64] = {0};
uint8_t hash[SHA256_DIGEST_LENGTH];
struct CborOut cbor_out;
CborOutInit(out, *out_length, &cbor_out);
// Make sure the number of items added below matches this num_pairs.
CborWriteMap(/*num_pairs=*/10, &cbor_out);
// Add mandatory field Issuer.
CborWriteInt(kIssuerLabel, &cbor_out);
if (entry_index == 0) {
snprintf(str_buf, sizeof(str_buf), "issuer 0");
} else {
// The issuer of current entry is the subject of its parent entry.
snprintf(str_buf, sizeof(str_buf), "entry %u", entry_index);
}
CborWriteTstr(str_buf, &cbor_out);
memset(str_buf, 0, sizeof(str_buf));
// Add mandatory field Subject.
CborWriteInt(kSubjectLabel, &cbor_out);
// Subject text starts with "entry 1"
snprintf(str_buf, sizeof(str_buf), "entry %u", entry_index + 1);
CborWriteTstr(str_buf, &cbor_out);
memset(str_buf, 0, sizeof(str_buf));
// Add the profile name.
CborWriteInt(kProfileNameLabel, &cbor_out);
if (is_leaf) {
snprintf(str_buf, sizeof(str_buf), "widevine.%d", API_MAJOR_VERSION);
CborWriteTstr(str_buf, &cbor_out);
memset(str_buf, 0, sizeof(str_buf));
} else {
CborWriteTstr("android.15", &cbor_out);
}
// Add the subject public key.
CborWriteInt(kSubjectPublicKeyLabel, &cbor_out);
CborWriteBstr(encoded_public_key_size, encoded_public_key, &cbor_out);
// Add the key usage.
CborWriteInt(kKeyUsageLabel, &cbor_out);
CborWriteBstr(/*data_size=*/1, &kKeyUsageCertSign, &cbor_out);
// Add the code hash.
CborWriteInt(kCodeHashLabel, &cbor_out);
snprintf(str_buf, sizeof(str_buf), "CodeToHash %u", entry_index);
OEMCryptoResult hash_res =
WTPI_C1_SHA256((const uint8_t*)str_buf, sizeof(str_buf), hash);
memset(str_buf, 0, sizeof(str_buf));
if (hash_res != OEMCrypto_SUCCESS) return hash_res;
CborWriteBstr(sizeof(hash), hash, &cbor_out);
memset(hash, 0, sizeof(hash));
// Add the configuration hash.
CborWriteInt(kConfigurationHashLabel, &cbor_out);
snprintf(str_buf, sizeof(str_buf), "ConfigurationToHash %u", entry_index);
hash_res = WTPI_C1_SHA256((const uint8_t*)str_buf, sizeof(str_buf), hash);
memset(str_buf, 0, sizeof(str_buf));
if (hash_res != OEMCrypto_SUCCESS) return hash_res;
CborWriteBstr(sizeof(hash), hash, &cbor_out);
memset(hash, 0, sizeof(hash));
// Add the configuration descriptor.
CborWriteInt(kConfigurationDescriptorLabel, &cbor_out);
uint8_t configuration_descriptor[DICE_MAX_CONFIGURATION_DESCRIPTOR_SIZE];
size_t configuration_descriptor_size = 0;
const DiceResult dice_result = EncodeConfigurationDescriptor(
sizeof(configuration_descriptor), configuration_descriptor,
&configuration_descriptor_size, entry_index, is_leaf);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
CborWriteBstr(configuration_descriptor_size, configuration_descriptor,
&cbor_out);
// Add the authority hash.
CborWriteInt(kAuthorityHashLabel, &cbor_out);
snprintf(str_buf, sizeof(str_buf), "AuthorityToHash %u", entry_index);
hash_res = WTPI_C1_SHA256((uint8_t*)str_buf, sizeof(str_buf), hash);
memset(str_buf, 0, sizeof(str_buf));
if (hash_res != OEMCrypto_SUCCESS) return hash_res;
CborWriteBstr(sizeof(hash), hash, &cbor_out);
memset(hash, 0, sizeof(hash));
// Add the mode.
CborWriteInt(kModeLabel, &cbor_out);
CborWriteBstr(/*data_size=*/1, &mode, &cbor_out);
if (CborOutOverflowed(&cbor_out)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
*out_length = CborOutSize(&cbor_out);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult GenerateCsrPayload(const uint8_t* challenge,
size_t challenge_size,
const uint8_t* encoded_device_info,
size_t encoded_device_info_size,
uint8_t* out, size_t* out_length) {
if (out == NULL || out_length == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (challenge == NULL && challenge_size != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (encoded_device_info == NULL && encoded_device_info_size != 0) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
// Write CsrPayload cbor struct
// CsrPayload = [ ; CBOR Array defining the payload for Csr
// version: 3, ; The CsrPayload CDDL Schema version
// CertificateType, ; The type of certificate being requested
// DeviceInfo, ; Defined in DeviceInfo.aidl
// KeysToSign, ; Provided by the method parameters
// ];
const int64_t kSchemaVersion = 3;
uint8_t csr_payload[CSR_PAYLOAD_MAX_LENGTH];
size_t csr_payload_size = sizeof(csr_payload);
struct CborOut cbor_csr_out;
CborOutInit(csr_payload, csr_payload_size, &cbor_csr_out);
CborWriteArray(/*num_elements=*/4, &cbor_csr_out);
CborWriteInt(kSchemaVersion, &cbor_csr_out);
// CertificateType
CborWriteTstr("widevine", &cbor_csr_out);
// DeviceInfo
OEMCryptoResult result = DecodeAndWriteDeviceInfo(
&cbor_csr_out, encoded_device_info, encoded_device_info_size);
if (result != OEMCrypto_SUCCESS) return result;
// KeysToSign
CborWriteArray(/*num_elements=*/0, &cbor_csr_out);
if (CborOutOverflowed(&cbor_csr_out)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
csr_payload_size = CborOutSize(&cbor_csr_out);
// Write SignedData<CsrPayload> cbor struct, which is to be signed by the BCC
// leaf cert.
// SignedData<CsrPayload> = [
// challenge: bstr .size (32..64),
// bstr .cbor CsrPayload,
// ];
struct CborOut cbor_signed_csr_out;
CborOutInit(out, *out_length, &cbor_signed_csr_out);
CborWriteArray(/*num_elements=*/2, &cbor_signed_csr_out);
// Challenge
CborWriteBstr(challenge_size, challenge, &cbor_signed_csr_out);
// CsrPayload
CborWriteBstr(csr_payload_size, csr_payload, &cbor_signed_csr_out);
if (CborOutOverflowed(&cbor_signed_csr_out)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
*out_length = CborOutSize(&cbor_signed_csr_out);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult BuildDegenerateBootCertificateChain(
const uint8_t* public_key, size_t public_key_length,
AsymmetricKeyType key_type, WTPI_AsymmetricKey_Handle key, uint8_t* out,
size_t* out_length) {
if (public_key == NULL || out_length == NULL || key == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t required_buffer_length = BCC_MAX_LENGTH;
// Can only support ED25519 and ECDSA P256
switch (key_type) {
case PROV40_ED25519_PRIVATE_KEY:
if (public_key_length != DICE_PUBLIC_KEY_SIZE_ED25519)
return OEMCrypto_ERROR_INVALID_CONTEXT;
break;
case DRM_ECC_PRIVATE_KEY:
if (public_key_length != DICE_PUBLIC_KEY_SIZE_P256)
return OEMCrypto_ERROR_INVALID_CONTEXT;
break;
default:
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (out == NULL || *out_length < required_buffer_length) {
*out_length = required_buffer_length;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
// Bcc = [
// COSE_Key, ; Root public key
// + COSE_Sign1, ; Bcc entries. Only one self-signed cert in phase 1.
// ];
// Write BCC array header.
struct CborOut cbor_out;
CborOutInit(out, *out_length, &cbor_out);
CborWriteArray(/*num_elements=*/2, &cbor_out);
if (CborOutOverflowed(&cbor_out)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t out_cursor = CborOutSize(&cbor_out);
// Encode first entry in the array which is a COSE_Key.
// The encoded public key will be copied to COSE_Sign1 later.
const uint8_t* encoded_public_key = out + out_cursor;
size_t encoded_public_key_size = 0;
DiceResult dice_result = DiceCoseEncodePublicKey(
public_key, public_key_length, *out_length - out_cursor, out + out_cursor,
&encoded_public_key_size, key_type);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
out_cursor += encoded_public_key_size;
// Encode second entry in the array which is a COSE_Sign1.
// The payload can not exceed total length.
uint8_t bcc_payload[BCC_MAX_LENGTH];
size_t bcc_payload_size = sizeof(bcc_payload);
OEMCryptoResult result = GenerateEncodedBccPayload(
encoded_public_key, encoded_public_key_size, /*entry_index=*/0, true,
bcc_payload, &bcc_payload_size);
if (result != OEMCrypto_SUCCESS) return result;
size_t encoded_cose_sign1_size = 0;
result = DiceCoseSignAndEncodeSign1(
bcc_payload, bcc_payload_size, key_type, key, *out_length - out_cursor,
out + out_cursor, &encoded_cose_sign1_size);
if (result != OEMCrypto_SUCCESS) return result;
out_cursor += encoded_cose_sign1_size;
*out_length = out_cursor;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult BuildBootCertificateChain(
const uint8_t* device_public_key, size_t device_public_key_length,
AsymmetricKeyType device_key_type,
WTPI_AsymmetricKey_Handle device_private_key,
const uint8_t (*entry_public_keys)[DICE_PUBLIC_KEY_SIZE_ED25519],
const WTPI_AsymmetricKey_Handle* entry_private_keys, size_t entry_count,
uint8_t* out, size_t* out_length) {
if (device_public_key == NULL || out_length == NULL ||
device_private_key == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (entry_public_keys == NULL || *entry_public_keys == NULL ||
entry_private_keys == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
if (entry_count == 0) return OEMCrypto_ERROR_INVALID_CONTEXT;
if (device_key_type != PROV40_ED25519_PRIVATE_KEY) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
if (device_public_key_length != DICE_PUBLIC_KEY_SIZE_ED25519) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
size_t required_buffer_length = BCC_MAX_LENGTH;
if (out == NULL || *out_length < required_buffer_length) {
*out_length = required_buffer_length;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
// Bcc = [
// COSE_Key, ; Root public key
// + COSE_Sign1, ; |entry_count| Bcc entries.
// ];
// Write BCC array header.
struct CborOut cbor_out;
CborOutInit(out, *out_length, &cbor_out);
CborWriteArray(1 + entry_count, &cbor_out);
if (CborOutOverflowed(&cbor_out)) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
size_t out_cursor = CborOutSize(&cbor_out);
// Encode first entry in the array which is a COSE_Key.
size_t encoded_device_public_key_size = 0;
DiceResult dice_result = DiceCoseEncodePublicKey(
device_public_key, device_public_key_length, *out_length - out_cursor,
out + out_cursor, &encoded_device_public_key_size, device_key_type);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
out_cursor += encoded_device_public_key_size;
// Encode |entry_count| entries. Each entry is a COSE_Sign1 and is signed with
// the private key of the previous entry. The first entry is signed by
// |device_private_key|.
const AsymmetricKeyType signing_key_type = device_key_type;
WTPI_AsymmetricKey_Handle signing_key = device_private_key;
for (size_t i = 0; i < entry_count; ++i) {
uint8_t encoded_entry_public_key[BCC_MAX_PAYLOAD_LENGTH];
size_t encoded_entry_public_key_size = 0;
dice_result = DiceCoseEncodePublicKey(
entry_public_keys[i], DICE_PUBLIC_KEY_SIZE_ED25519,
sizeof(encoded_entry_public_key), encoded_entry_public_key,
&encoded_entry_public_key_size, PROV40_ED25519_PRIVATE_KEY);
if (dice_result != kDiceResultOk) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
uint8_t bcc_payload[BCC_MAX_PAYLOAD_LENGTH];
size_t bcc_payload_size = sizeof(bcc_payload);
const bool is_leaf = (i == entry_count - 1);
OEMCryptoResult result = GenerateEncodedBccPayload(
encoded_entry_public_key, encoded_entry_public_key_size,
/*entry_index=*/(uint32_t)i, is_leaf, bcc_payload, &bcc_payload_size);
if (result != OEMCrypto_SUCCESS) return result;
size_t encoded_cose_sign1_size = 0;
result = DiceCoseSignAndEncodeSign1(
bcc_payload, bcc_payload_size, signing_key_type, signing_key,
*out_length - out_cursor, out + out_cursor, &encoded_cose_sign1_size);
if (result != OEMCrypto_SUCCESS) return result;
out_cursor += encoded_cose_sign1_size;
signing_key = entry_private_keys[i];
}
*out_length = out_cursor;
return OEMCrypto_SUCCESS;
}
OEMCryptoResult GetDevicePublicKeyFromBcc(const uint8_t* bcc, size_t bcc_length,
uint8_t* dk_pub,
size_t* dk_pub_length) {
if (bcc == NULL || bcc_length == 0 || dk_pub_length == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
struct CborIn in;
CborInInit(bcc, bcc_length, &in);
size_t bcc_item_count = 0;
enum CborReadResult res = CborReadArray(&in, &bcc_item_count);
if (res != CBOR_READ_RESULT_OK) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (bcc_item_count < 2) {
// There should at least be the public key and one entry.
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
// The first item in the BCC array is the device public key we want.
const size_t bcc_items_offset = CborInOffset(&in);
// Skip the first item to know the size of the first item.
res = CborReadSkip(&in);
if (res != CBOR_READ_RESULT_OK) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
const size_t bcc_first_item_size = CborInOffset(&in) - bcc_items_offset;
if (*dk_pub_length < bcc_first_item_size) {
*dk_pub_length = bcc_first_item_size;
return OEMCrypto_ERROR_SHORT_BUFFER;
}
if (dk_pub == NULL) {
return OEMCrypto_ERROR_INVALID_CONTEXT;
}
memcpy(dk_pub, bcc + bcc_items_offset, bcc_first_item_size);
*dk_pub_length = bcc_first_item_size;
return OEMCrypto_SUCCESS;
}