aboutsummaryrefslogtreecommitdiff
path: root/secure_fw/partitions/initial_attestation/attest_symmetric_key.c
blob: cdc8a3e681cb26be70619efd9ca2d95d6d5608f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
 * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
 * Copyright (c) 2018-2019, Laurence Lundblade.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include <stddef.h>
#include <stdint.h>

#include "attestation_key.h"
#include "platform/include/tfm_plat_crypto_keys.h"
#include "psa/crypto.h"
#include "tfm_memory_utils.h"

/* Only support HMAC as MAC algorithm in COSE_Mac0 so far */
#define SYMMETRIC_IAK_MAX_SIZE        PSA_MAC_MAX_SIZE

/* Hash algorithm for calculating Instance ID */
#define INSTANCE_ID_HASH_ALG          PSA_ALG_SHA_256

/* Length of kid buffer */
#define KID_BUF_LEN                   32

/* Symmetric IAK handle */
static psa_key_handle_t symmetric_iak_handle = 0;

/* Instance ID for symmetric IAK */
static uint8_t instance_id_buf[PSA_HASH_SIZE(INSTANCE_ID_HASH_ALG) + 1];
static size_t instance_id_len = 0;

#ifdef INCLUDE_COSE_KEY_ID
/* kid buffer */
static uint8_t kid_buf[KID_BUF_LEN];
/* Actual kid length */
static size_t kid_len = 0;
#endif

static psa_status_t destroy_iak(psa_key_handle_t *iak_handle)
{
    psa_status_t res;

    res = psa_destroy_key(*iak_handle);

    *iak_handle = 0;
    return res;
}

/**
 * \brief Static function to execute hash computation of symmetric IAK once.
 *
 * \param[in]  iak_buf      Buffer containing the IAK raw data
 * \param[in]  iak_len      The length of IAK raw data
 * \param[out] hash_buf     The buffer to be written with hash data
 * \param[in]  hash_size    The size of hash_buf
 * \param[out] hash_len     The actual length of hash data
 *
 * \return Returns error code as specified in \ref psa_status_t
 */
static psa_status_t symmetric_iak_hash(const uint8_t *iak_buf,
                                       size_t        iak_len,
                                       uint8_t       *hash_buf,
                                       size_t        hash_size,
                                       size_t        *hash_len)
{
    psa_hash_operation_t hash_op = psa_hash_operation_init();
    psa_status_t status;

    if (!iak_buf || !hash_buf || !hash_len) {
        return PSA_ERROR_INVALID_ARGUMENT;
    }

    status = psa_hash_setup(&hash_op, INSTANCE_ID_HASH_ALG);
    if (status != PSA_SUCCESS) {
        return status;
    }

    status = psa_hash_update(&hash_op, iak_buf, iak_len);
    if (status != PSA_SUCCESS) {
        return status;
    }

    status = psa_hash_finish(&hash_op, hash_buf, hash_size, hash_len);

    return status;
}

/*
 * Hash a symmetric Initial Attestation Key (IAK) twice to get the Instance ID.
 *
 * \note Please note that this function will corrupt the original IAK data in
 *       iak_buf.
 *       It can save a 32-byte buffer to put the intermediate data of the first
 *       hash into iak_buf.
 */
static psa_status_t calc_instance_id(uint8_t *iak_buf, size_t iak_len)
{
    psa_status_t status;
    /* Leave the first byte for UEID type byte */
    uint8_t *id_ptr = instance_id_buf + 1;
    size_t id_len = sizeof(instance_id_buf) - 1;

    if (!iak_buf) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    status = symmetric_iak_hash(iak_buf, iak_len, id_ptr, id_len,
                                &instance_id_len);
    if (status != PSA_SUCCESS) {
        instance_id_len = 0;
        return status;
    }

    /*
     * instance_id_len = SHA-256 block size < key_len <= key_buf size
     * It should be safe to directly copy without boundary check.
     */
    tfm_memcpy(iak_buf, id_ptr, instance_id_len);

    status = symmetric_iak_hash(iak_buf, instance_id_len, id_ptr, id_len,
                                &instance_id_len);
    if (status == PSA_SUCCESS) {
        /* Add UEID type byte 0x01 */
        instance_id_buf[0] = 0x01;
        instance_id_len++;
    } else {
        instance_id_len = 0;
    }

    return status;
}

enum psa_attest_err_t attest_register_initial_attestation_key(void)
{
    uint8_t key_buf[SYMMETRIC_IAK_MAX_SIZE];
    psa_algorithm_t key_alg;
    psa_key_handle_t key_handle;
    size_t key_len;
    enum tfm_plat_err_t plat_res;
    psa_status_t psa_res;
    psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;

    if (symmetric_iak_handle) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    /* Get the symmetric initial attestation key for HMAC operation */
    plat_res = tfm_plat_get_symmetric_iak(key_buf, sizeof(key_buf),
                                          &key_len, &key_alg);
    /* In case the buffer size was not checked, although unlikely */
    if (sizeof(key_buf) < key_len) {
        /*
         * Something critical following key_buf may be overwritten.
         * Directly jump into fatal error handling.
         *
         * TODO: Should be replaced by a call to psa_panic() when it
         * becomes available.
         */
        while (1) {
            ;
        }
    }
    if (plat_res != TFM_PLAT_ERR_SUCCESS) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    /*
     * Verify if HMAC algorithm is valid.
     * According to COSE (RFC 8152), only SHA-256, SHA-384 and SHA-512 are
     * supported in HMAC.
     */
    if ((key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_256)) &&
        (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_384)) &&
        (key_alg != PSA_ALG_HMAC(PSA_ALG_SHA_512))) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    /* Setup the key attributes */
    psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_SIGN);
    psa_set_key_algorithm(&key_attributes, key_alg);
    psa_set_key_type(&key_attributes, PSA_KEY_TYPE_HMAC);

    /* Register the symmetric key to Crypto service */
    psa_res = psa_import_key(&key_attributes, key_buf, key_len, &key_handle);
    if (psa_res != PSA_SUCCESS) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    symmetric_iak_handle = key_handle;

    /*
     * Calculate the Instance ID.
     * Since Instance ID is generated from symmetric IAK, achieve it now to
     * protect critical IAK raw data from being repeatedly fetched.
     * IAK in key_buf will be corrupted. Therefore, this step must be called
     * at the end.
     */
    psa_res = calc_instance_id(key_buf, key_len);
    if (psa_res != PSA_SUCCESS) {
        destroy_iak(&symmetric_iak_handle);
        return PSA_ATTEST_ERR_GENERAL;
    }

    return PSA_ATTEST_ERR_SUCCESS;
}

enum psa_attest_err_t attest_unregister_initial_attestation_key(void)
{
    if (!symmetric_iak_handle) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    destroy_iak(&symmetric_iak_handle);

    /* Invalidate the Instance ID as well */
    instance_id_len = 0;

#ifdef INCLUDE_COSE_KEY_ID
    /* Invalidate the corresponding kid as well */
    kid_len = 0;
#endif

    return PSA_ATTEST_ERR_SUCCESS;
}

enum psa_attest_err_t
attest_get_signing_key_handle(psa_key_handle_t *key_handle)
{
    if (!symmetric_iak_handle) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    *key_handle = symmetric_iak_handle;

    return PSA_ATTEST_ERR_SUCCESS;
}

enum psa_attest_err_t
attest_get_instance_id(struct q_useful_buf_c *id_buf)
{
    if (!id_buf) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    if (!instance_id_len) {
        return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
    }

    id_buf->ptr = instance_id_buf;
    id_buf->len = instance_id_len;

    return PSA_ATTEST_ERR_SUCCESS;
}

#ifdef INCLUDE_COSE_KEY_ID
enum psa_attest_err_t
attest_get_initial_attestation_key_id(struct q_useful_buf_c *attest_key_id)
{
    enum tfm_plat_err_t plat_res;

    if (!attest_key_id) {
        return PSA_ATTEST_ERR_GENERAL;
    }

    /* The kid has not been fetched previously */
    if (!kid_len) {
        plat_res = tfm_plat_get_symmetric_iak_id(kid_buf,
                                                 sizeof(kid_buf),
                                                 &kid_len);
        /* In case the buffer size was not checked, although unlikely */
        if (sizeof(kid_buf) < kid_len) {
            /*
             * Something critical following kid_buf may be overwritten.
             * Directly jump into fatal error handling.
             *
             * TODO: Should be replaced by a call to psa_panic() when it
             * becomes available.
             */
            while (1) {
                ;
            }
        }

        if (plat_res != TFM_PLAT_ERR_SUCCESS) {
            return PSA_ATTEST_ERR_GENERAL;
        }
    }

    attest_key_id->ptr = (const void *)&kid_buf;
    attest_key_id->len = kid_len;

    return PSA_ATTEST_ERR_SUCCESS;
}
#endif /* INCLUDE_COSE_KEY_ID */