| // 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. |
| |
| // If no variable length descriptors are used in a DICE certificate, the |
| // certificate can be constructed from a template instead of using a CBOR |
| // library. This implementation includes only hashes and inline configuration in |
| // the DICE extension. For convenience this uses only the lower level curve25519 |
| // implementation in boringssl. This approach may be especially useful in very |
| // low level components where simplicity is paramount. |
| |
| // This is an implementation of the DiceGenerateCertificate that generates a |
| // CWT-style CBOR certificate based on a template using the ED25519-SHA512 |
| // signature scheme. |
| // |
| // If no variable length descriptors are used in a DICE certificate, the |
| // certificate can be constructed from a template instead of using a CBOR / |
| // COSE library. This implementation includes only hashes and inline |
| // configuration in the certificate fields. This approach may be especially |
| // useful in very low level components where simplicity is paramount. |
| // |
| // This function will return kDiceResultInvalidInput if 'input_values' specifies |
| // any variable length descriptors. In particular: |
| // * code_descriptor_size must be zero |
| // * authority_descriptor_size must be zero |
| // * config_type must be kDiceConfigTypeInline |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "dice/dice.h" |
| #include "dice/ops.h" |
| #include "dice/utils.h" |
| |
| #if DICE_PUBLIC_KEY_BUFFER_SIZE != 32 |
| #error "Only Ed25519 is supported; 32 bytes needed to store the public key." |
| #endif |
| #if DICE_SIGNATURE_BUFFER_SIZE != 64 |
| #error "Only Ed25519 is supported; 64 bytes needed to store the signature." |
| #endif |
| |
| // 20 bytes of header, 366 bytes of payload. |
| #define DICE_TBS_SIZE 386 |
| |
| // A well-formed certificate, but with zeros in all fields to be filled. |
| static const uint8_t kTemplate[441] = { |
| // Constant encoding. |
| 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x6e, |
| // Offset 9: Payload starts here, 366 bytes. |
| 0xa8, 0x01, 0x78, 0x28, |
| // Offset 13: CWT issuer, 40 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding. |
| 0x02, 0x78, 0x28, |
| // Offset 56: CWT subject, 40 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding. |
| 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, |
| // Offset 103: Code hash, 64 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding. |
| 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x40, |
| // Offset 174: Configuration value, 64 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding. |
| 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, |
| // Offset 245: Authority hash, 64 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding. |
| 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, |
| // Offset 315: Mode, 1 byte. |
| 0x00, |
| // Constant encoding. |
| 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, |
| 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, |
| // Offset 336: Public key, 32 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| // Constant encoding (key usage). |
| 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, |
| // Offset 375: Payload ends here. |
| // Constant encoding. |
| 0x58, 0x40, |
| // Offset 377: Signature, 64 bytes. |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00}; |
| |
| // The data to be signed is not the certificate, but the payload appended to |
| // this header. This is the 'Sig_structure' for COSE_Sign1, per RFC 8152. |
| static const uint8_t kTbsHeader[20] = {0x84, 0x6a, 0x53, 0x69, 0x67, 0x6e, 0x61, |
| 0x74, 0x75, 0x72, 0x65, 0x31, 0x43, 0xa1, |
| 0x01, 0x27, 0x40, 0x59, 0x01, 0x6e}; |
| |
| static const struct { |
| size_t offset; |
| size_t length; |
| } kFieldTable[] = {{13, 40}, // Issuer |
| {56, 40}, // Subject |
| {103, 64}, // Code hash |
| {174, 64}, // Config descriptor |
| {245, 64}, // Authority hash |
| {315, 1}, // Mode |
| {336, 32}, // Public key |
| {377, 64}, // Signature |
| {9, 366}}; // Payload |
| |
| static const size_t kFieldIndexIssuer = 0; |
| static const size_t kFieldIndexSubject = 1; |
| static const size_t kFieldIndexCodeHash = 2; |
| static const size_t kFieldIndexConfigDescriptor = 3; |
| static const size_t kFieldIndexAuthorityHash = 4; |
| static const size_t kFieldIndexMode = 5; |
| static const size_t kFieldIndexSubjectPublicKey = 6; |
| static const size_t kFieldIndexSignature = 7; |
| static const size_t kFieldIndexPayload = 8; |
| |
| // |buffer| must point to the beginning of the template buffer and |src| must |
| // point to at least <field-length> bytes. |
| static void CopyField(const uint8_t* src, size_t index, uint8_t* buffer) { |
| memcpy(&buffer[kFieldTable[index].offset], src, kFieldTable[index].length); |
| } |
| |
| 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) { |
| DiceResult result = kDiceResultOk; |
| |
| DiceKeyParam key_param; |
| result = DiceGetKeyParam(context, kDicePrincipalSubject, &key_param); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| |
| // Variable length descriptors are not supported. |
| if (input_values->code_descriptor_size > 0 || |
| input_values->config_type != kDiceConfigTypeInline || |
| input_values->authority_descriptor_size > 0 || key_param.profile_name) { |
| return kDiceResultInvalidInput; |
| } |
| |
| // We know the certificate size upfront so we can do the buffer size check. |
| *certificate_actual_size = sizeof(kTemplate); |
| if (certificate_buffer_size < sizeof(kTemplate)) { |
| return kDiceResultBufferTooSmall; |
| } |
| |
| // Declare buffers which are cleared on 'goto out'. |
| uint8_t subject_private_key[DICE_PRIVATE_KEY_BUFFER_SIZE]; |
| uint8_t authority_private_key[DICE_PRIVATE_KEY_BUFFER_SIZE]; |
| |
| // Derive keys and IDs from the private key seeds. |
| uint8_t subject_public_key[DICE_PUBLIC_KEY_BUFFER_SIZE]; |
| result = DiceKeypairFromSeed(context, kDicePrincipalSubject, |
| subject_private_key_seed, subject_public_key, |
| subject_private_key); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| |
| uint8_t subject_id[DICE_ID_SIZE]; |
| result = DiceDeriveCdiCertificateId(context, subject_public_key, |
| DICE_PUBLIC_KEY_BUFFER_SIZE, subject_id); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| uint8_t subject_id_hex[40]; |
| DiceHexEncode(subject_id, sizeof(subject_id), subject_id_hex, |
| sizeof(subject_id_hex)); |
| |
| uint8_t authority_public_key[DICE_PUBLIC_KEY_BUFFER_SIZE]; |
| result = DiceKeypairFromSeed(context, kDicePrincipalAuthority, |
| authority_private_key_seed, authority_public_key, |
| authority_private_key); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| |
| uint8_t authority_id[DICE_ID_SIZE]; |
| result = DiceDeriveCdiCertificateId( |
| context, authority_public_key, DICE_PUBLIC_KEY_BUFFER_SIZE, authority_id); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| uint8_t authority_id_hex[40]; |
| DiceHexEncode(authority_id, sizeof(authority_id), authority_id_hex, |
| sizeof(authority_id_hex)); |
| |
| // First copy in the entire template, then fill in the fields. |
| memcpy(certificate, kTemplate, sizeof(kTemplate)); |
| CopyField(authority_id_hex, kFieldIndexIssuer, certificate); |
| CopyField(subject_id_hex, kFieldIndexSubject, certificate); |
| CopyField(subject_public_key, kFieldIndexSubjectPublicKey, certificate); |
| CopyField(input_values->code_hash, kFieldIndexCodeHash, certificate); |
| CopyField(input_values->config_value, kFieldIndexConfigDescriptor, |
| certificate); |
| CopyField(input_values->authority_hash, kFieldIndexAuthorityHash, |
| certificate); |
| certificate[kFieldTable[kFieldIndexMode].offset] = input_values->mode; |
| |
| // Fill the TBS structure using the payload from the certificate. |
| uint8_t tbs[DICE_TBS_SIZE]; |
| memcpy(tbs, kTbsHeader, sizeof(kTbsHeader)); |
| memcpy(&tbs[sizeof(kTbsHeader)], |
| &certificate[kFieldTable[kFieldIndexPayload].offset], |
| kFieldTable[kFieldIndexPayload].length); |
| |
| uint8_t signature[DICE_SIGNATURE_BUFFER_SIZE]; |
| result = |
| DiceSign(context, tbs, sizeof(tbs), authority_private_key, signature); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| result = |
| DiceVerify(context, tbs, sizeof(tbs), signature, authority_public_key); |
| if (result != kDiceResultOk) { |
| goto out; |
| } |
| CopyField(signature, kFieldIndexSignature, certificate); |
| |
| out: |
| DiceClearMemory(context, sizeof(subject_private_key), subject_private_key); |
| DiceClearMemory(context, sizeof(authority_private_key), |
| authority_private_key); |
| return result; |
| } |