Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 1 | /* |
| 2 | * attest_token.c |
| 3 | * |
| 4 | * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved. |
| 5 | * |
| 6 | * SPDX-License-Identifier: BSD-3-Clause |
| 7 | * |
| 8 | * See BSD-3-Clause license in README.md |
| 9 | */ |
| 10 | |
| 11 | #include "attest_token.h" |
| 12 | #include "qcbor.h" |
| 13 | #include "t_cose_sign1_sign.h" |
| 14 | |
| 15 | |
| 16 | /** |
| 17 | * \file attest_token.c |
| 18 | * |
| 19 | * \brief Attestation token creation implementation |
| 20 | * |
| 21 | * Outline of token creation. Much of this occurs inside |
| 22 | * t_cose_sign1_init() and t_cose_sign1_finish(). |
| 23 | * |
| 24 | * - Create encoder context |
| 25 | * - Open the CBOR array that hold the \c COSE_Sign1 |
| 26 | * - Write COSE Headers |
| 27 | * - Protected Header |
| 28 | * - Algorithm ID |
| 29 | * - Unprotected Headers |
| 30 | * - Key ID |
| 31 | * - Open payload bstr |
| 32 | * - Write payload data… lots of it… |
| 33 | * - Get bstr that is the encoded payload |
| 34 | * - Compute signature |
| 35 | * - Create a separate encoder context for \c Sig_structure |
| 36 | * - Encode CBOR context identifier |
| 37 | * - Encode protected headers |
| 38 | * - Encode two empty bstr |
| 39 | * - Add one more empty bstr that is a "fake payload" |
| 40 | * - Close off \c Sig_structure |
| 41 | * - Hash all but "fake payload" of \c Sig_structure |
| 42 | * - Get payload bstr ptr and length |
| 43 | * - Continue hash of the real encoded payload |
| 44 | * - Run ECDSA |
| 45 | * - Write signature into the CBOR output |
| 46 | * - Close CBOR array holding the \c COSE_Sign1 |
| 47 | */ |
| 48 | |
| 49 | /* |
| 50 | * \brief Map t_cose error to attestation token error. |
| 51 | * |
| 52 | * \param[in] err The t_cose error to map. |
| 53 | * |
| 54 | * \return the attestation token error. |
| 55 | * |
| 56 | */ |
| 57 | static enum attest_token_err_t t_cose_err_to_attest_err(enum t_cose_err_t err) |
| 58 | { |
| 59 | switch(err) { |
| 60 | |
| 61 | case T_COSE_SUCCESS: |
| 62 | return ATTEST_TOKEN_ERR_SUCCESS; |
| 63 | |
| 64 | case T_COSE_ERR_UNSUPPORTED_HASH: |
| 65 | return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE; |
| 66 | |
| 67 | default: |
| 68 | /* A lot of the errors are not mapped because they are |
| 69 | * primarily internal errors that should never happen. They |
| 70 | * end up here. |
| 71 | */ |
| 72 | return ATTEST_TOKEN_ERR_GENERAL; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | |
| 77 | /* |
| 78 | Public function. See attest_token.h |
| 79 | */ |
| 80 | enum attest_token_err_t attest_token_start(struct attest_token_ctx *me, |
| 81 | uint32_t opt_flags, |
| 82 | int32_t key_select, |
| 83 | int32_t cose_alg_id, |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 84 | const struct q_useful_buf *out_buf) |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 85 | { |
| 86 | /* approximate stack usage on 32-bit machine: 4 bytes */ |
| 87 | enum t_cose_err_t cose_return_value; |
| 88 | enum attest_token_err_t return_value; |
| 89 | |
| 90 | /* Remember some of the configuration values */ |
| 91 | me->opt_flags = opt_flags; |
| 92 | me->key_select = key_select; |
| 93 | |
| 94 | /* Spin up the CBOR encoder */ |
| 95 | QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf); |
| 96 | |
| 97 | |
| 98 | /* Initialize COSE signer. This will cause the cose headers to be |
| 99 | * encoded and written into out_buf using me->cbor_enc_ctx |
| 100 | */ |
| 101 | cose_return_value = t_cose_sign1_init(&(me->signer_ctx), |
| 102 | opt_flags & |
| 103 | TOKEN_OPT_SHORT_CIRCUIT_SIGN, |
| 104 | cose_alg_id, |
| 105 | key_select, |
| 106 | &(me->cbor_enc_ctx)); |
| 107 | if(cose_return_value) { |
| 108 | return_value = t_cose_err_to_attest_err(cose_return_value); |
| 109 | goto Done; |
| 110 | } |
| 111 | |
| 112 | /* Open the payload-wrapping bstr */ |
| 113 | QCBOREncode_BstrWrap(&(me->cbor_enc_ctx)); |
| 114 | |
| 115 | QCBOREncode_OpenMap(&(me->cbor_enc_ctx)); |
| 116 | |
| 117 | return_value = ATTEST_TOKEN_ERR_SUCCESS; |
| 118 | |
| 119 | Done: |
| 120 | return return_value; |
| 121 | } |
| 122 | |
| 123 | |
| 124 | /* |
| 125 | Public function. See attest_token.h |
| 126 | */ |
| 127 | QCBOREncodeContext *attest_token_borrow_cbor_cntxt(struct attest_token_ctx *me) |
| 128 | { |
| 129 | return &(me->cbor_enc_ctx); |
| 130 | } |
| 131 | |
| 132 | |
| 133 | /* |
| 134 | Public function. See attest_token.h |
| 135 | */ |
| 136 | void attest_token_add_integer(struct attest_token_ctx *me, |
| 137 | int32_t label, |
| 138 | int64_t Value) |
| 139 | { |
| 140 | QCBOREncode_AddInt64ToMapN(&(me->cbor_enc_ctx), label, Value); |
| 141 | } |
| 142 | |
| 143 | |
| 144 | /* |
| 145 | Public function. See attest_token.h |
| 146 | */ |
| 147 | void attest_token_add_bstr(struct attest_token_ctx *me, |
| 148 | int32_t label, |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 149 | const struct q_useful_buf_c *bstr) |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 150 | { |
| 151 | QCBOREncode_AddBytesToMapN(&(me->cbor_enc_ctx), |
| 152 | label, |
| 153 | *bstr); |
| 154 | } |
| 155 | |
| 156 | |
| 157 | /* |
| 158 | Public function. See attest_token.h |
| 159 | */ |
| 160 | void attest_token_add_tstr(struct attest_token_ctx *me, |
| 161 | int32_t label, |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 162 | const struct q_useful_buf_c *tstr) |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 163 | { |
| 164 | QCBOREncode_AddTextToMapN(&(me->cbor_enc_ctx), label, *tstr); |
| 165 | } |
| 166 | |
| 167 | |
| 168 | /* |
| 169 | See attest_token.h |
| 170 | */ |
| 171 | void attest_token_add_encoded(struct attest_token_ctx *me, |
| 172 | int32_t label, |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 173 | const struct q_useful_buf_c *encoded) |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 174 | { |
| 175 | QCBOREncode_AddEncodedToMapN(&(me->cbor_enc_ctx), label, *encoded); |
| 176 | } |
| 177 | |
| 178 | |
| 179 | /* |
| 180 | Public function. See attest_token.h |
| 181 | */ |
| 182 | enum attest_token_err_t |
| 183 | attest_token_finish(struct attest_token_ctx *me, |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 184 | struct q_useful_buf_c *completed_token) |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 185 | { |
| 186 | /* approximate stack usage on 32-bit machine: 4 + 4 + 8 + 8 = 24 */ |
| 187 | enum attest_token_err_t return_value = ATTEST_TOKEN_ERR_SUCCESS; |
| 188 | /* The payload with all the claims that is signed */ |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 189 | struct q_useful_buf_c token_payload_ub; |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 190 | /* The completed and signed encoded cose_sign1 */ |
Laurence Lundblade | e1610ad | 2019-02-20 13:53:20 -0800 | [diff] [blame^] | 191 | struct q_useful_buf_c completed_token_ub; |
Laurence Lundblade | affd65a | 2018-12-18 10:50:48 -0800 | [diff] [blame] | 192 | QCBORError qcbor_result; |
| 193 | enum t_cose_err_t cose_return_value; |
| 194 | |
| 195 | QCBOREncode_CloseMap(&(me->cbor_enc_ctx)); |
| 196 | |
| 197 | /* Close off the payload-wrapping bstr. This gives us back the |
| 198 | * pointer and length of the payload that needs to be hashed as |
| 199 | * part of the signature |
| 200 | */ |
| 201 | QCBOREncode_CloseBstrWrap(&(me->cbor_enc_ctx), &token_payload_ub); |
| 202 | |
| 203 | /* Finish off the cose signature. This does all the interesting work of |
| 204 | hashing and signing */ |
| 205 | cose_return_value = |
| 206 | t_cose_sign1_finish(&(me->signer_ctx), token_payload_ub); |
| 207 | if(cose_return_value) { |
| 208 | /* Main errors are invoking the hash or signature */ |
| 209 | return_value = t_cose_err_to_attest_err(cose_return_value); |
| 210 | goto Done; |
| 211 | } |
| 212 | |
| 213 | /* Close off the CBOR encoding and return the completed token */ |
| 214 | qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx), |
| 215 | &completed_token_ub); |
| 216 | if(qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) { |
| 217 | return_value = ATTEST_TOKEN_ERR_TOO_SMALL; |
| 218 | } else if (qcbor_result != QCBOR_SUCCESS) { |
| 219 | /* likely from array not closed, too many closes, ... */ |
| 220 | return_value = ATTEST_TOKEN_ERR_CBOR_FORMATTING; |
| 221 | } else { |
| 222 | *completed_token = completed_token_ub; |
| 223 | } |
| 224 | |
| 225 | Done: |
| 226 | return return_value; |
| 227 | } |
| 228 | |