| /* |
| * attest_token.c |
| * |
| * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| * See BSD-3-Clause license in README.md |
| */ |
| |
| #include "attest_token.h" |
| #include "qcbor.h" |
| #include "t_cose_sign1_sign.h" |
| |
| |
| /** |
| * \file attest_token.c |
| * |
| * \brief Attestation token creation implementation |
| * |
| * Outline of token creation. Much of this occurs inside |
| * t_cose_sign1_init() and t_cose_sign1_finish(). |
| * |
| * - Create encoder context |
| * - Open the CBOR array that hold the \c COSE_Sign1 |
| * - Write COSE Headers |
| * - Protected Header |
| * - Algorithm ID |
| * - Unprotected Headers |
| * - Key ID |
| * - Open payload bstr |
| * - Write payload data… lots of it… |
| * - Get bstr that is the encoded payload |
| * - Compute signature |
| * - Create a separate encoder context for \c Sig_structure |
| * - Encode CBOR context identifier |
| * - Encode protected headers |
| * - Encode two empty bstr |
| * - Add one more empty bstr that is a "fake payload" |
| * - Close off \c Sig_structure |
| * - Hash all but "fake payload" of \c Sig_structure |
| * - Get payload bstr ptr and length |
| * - Continue hash of the real encoded payload |
| * - Run ECDSA |
| * - Write signature into the CBOR output |
| * - Close CBOR array holding the \c COSE_Sign1 |
| */ |
| |
| /* |
| * \brief Map t_cose error to attestation token error. |
| * |
| * \param[in] err The t_cose error to map. |
| * |
| * \return the attestation token error. |
| * |
| */ |
| static enum attest_token_err_t t_cose_err_to_attest_err(enum t_cose_err_t err) |
| { |
| switch(err) { |
| |
| case T_COSE_SUCCESS: |
| return ATTEST_TOKEN_ERR_SUCCESS; |
| |
| case T_COSE_ERR_UNSUPPORTED_HASH: |
| return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE; |
| |
| default: |
| /* A lot of the errors are not mapped because they are |
| * primarily internal errors that should never happen. They |
| * end up here. |
| */ |
| return ATTEST_TOKEN_ERR_GENERAL; |
| } |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| enum attest_token_err_t attest_token_start(struct attest_token_ctx *me, |
| uint32_t opt_flags, |
| int32_t key_select, |
| int32_t cose_alg_id, |
| const struct q_useful_buf *out_buf) |
| { |
| /* approximate stack usage on 32-bit machine: 4 bytes */ |
| enum t_cose_err_t cose_return_value; |
| enum attest_token_err_t return_value; |
| |
| /* Remember some of the configuration values */ |
| me->opt_flags = opt_flags; |
| me->key_select = key_select; |
| |
| /* Spin up the CBOR encoder */ |
| QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf); |
| |
| |
| /* Initialize COSE signer. This will cause the cose headers to be |
| * encoded and written into out_buf using me->cbor_enc_ctx |
| */ |
| cose_return_value = t_cose_sign1_init(&(me->signer_ctx), |
| opt_flags & |
| TOKEN_OPT_SHORT_CIRCUIT_SIGN, |
| cose_alg_id, |
| key_select, |
| &(me->cbor_enc_ctx)); |
| if(cose_return_value) { |
| return_value = t_cose_err_to_attest_err(cose_return_value); |
| goto Done; |
| } |
| |
| /* Open the payload-wrapping bstr */ |
| QCBOREncode_BstrWrap(&(me->cbor_enc_ctx)); |
| |
| QCBOREncode_OpenMap(&(me->cbor_enc_ctx)); |
| |
| return_value = ATTEST_TOKEN_ERR_SUCCESS; |
| |
| Done: |
| return return_value; |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| QCBOREncodeContext *attest_token_borrow_cbor_cntxt(struct attest_token_ctx *me) |
| { |
| return &(me->cbor_enc_ctx); |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| void attest_token_add_integer(struct attest_token_ctx *me, |
| int32_t label, |
| int64_t Value) |
| { |
| QCBOREncode_AddInt64ToMapN(&(me->cbor_enc_ctx), label, Value); |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| void attest_token_add_bstr(struct attest_token_ctx *me, |
| int32_t label, |
| const struct q_useful_buf_c *bstr) |
| { |
| QCBOREncode_AddBytesToMapN(&(me->cbor_enc_ctx), |
| label, |
| *bstr); |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| void attest_token_add_tstr(struct attest_token_ctx *me, |
| int32_t label, |
| const struct q_useful_buf_c *tstr) |
| { |
| QCBOREncode_AddTextToMapN(&(me->cbor_enc_ctx), label, *tstr); |
| } |
| |
| |
| /* |
| See attest_token.h |
| */ |
| void attest_token_add_encoded(struct attest_token_ctx *me, |
| int32_t label, |
| const struct q_useful_buf_c *encoded) |
| { |
| QCBOREncode_AddEncodedToMapN(&(me->cbor_enc_ctx), label, *encoded); |
| } |
| |
| |
| /* |
| Public function. See attest_token.h |
| */ |
| enum attest_token_err_t |
| attest_token_finish(struct attest_token_ctx *me, |
| struct q_useful_buf_c *completed_token) |
| { |
| /* approximate stack usage on 32-bit machine: 4 + 4 + 8 + 8 = 24 */ |
| enum attest_token_err_t return_value = ATTEST_TOKEN_ERR_SUCCESS; |
| /* The payload with all the claims that is signed */ |
| struct q_useful_buf_c token_payload_ub; |
| /* The completed and signed encoded cose_sign1 */ |
| struct q_useful_buf_c completed_token_ub; |
| QCBORError qcbor_result; |
| enum t_cose_err_t cose_return_value; |
| |
| QCBOREncode_CloseMap(&(me->cbor_enc_ctx)); |
| |
| /* Close off the payload-wrapping bstr. This gives us back the |
| * pointer and length of the payload that needs to be hashed as |
| * part of the signature |
| */ |
| QCBOREncode_CloseBstrWrap(&(me->cbor_enc_ctx), &token_payload_ub); |
| |
| /* Finish off the cose signature. This does all the interesting work of |
| hashing and signing */ |
| cose_return_value = |
| t_cose_sign1_finish(&(me->signer_ctx), token_payload_ub); |
| if(cose_return_value) { |
| /* Main errors are invoking the hash or signature */ |
| return_value = t_cose_err_to_attest_err(cose_return_value); |
| goto Done; |
| } |
| |
| /* Close off the CBOR encoding and return the completed token */ |
| qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx), |
| &completed_token_ub); |
| if(qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) { |
| return_value = ATTEST_TOKEN_ERR_TOO_SMALL; |
| } else if (qcbor_result != QCBOR_SUCCESS) { |
| /* likely from array not closed, too many closes, ... */ |
| return_value = ATTEST_TOKEN_ERR_CBOR_FORMATTING; |
| } else { |
| *completed_token = completed_token_ub; |
| } |
| |
| Done: |
| return return_value; |
| } |
| |