758 lines
28 KiB
C
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;
|
|
}
|