1 /*
2  * Copyright (c) 2017-2022, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "ps_crypto_interface.h"
9 
10 #include <stdbool.h>
11 #include <string.h>
12 
13 #include "config_tfm.h"
14 #include "tfm_crypto_defs.h"
15 #include "psa/crypto.h"
16 
17 #ifndef PS_CRYPTO_AEAD_ALG
18 #define PS_CRYPTO_AEAD_ALG PSA_ALG_GCM
19 #endif
20 
21 /* The PSA key type used by this implementation */
22 #define PS_KEY_TYPE PSA_KEY_TYPE_AES
23 /* The PSA key usage required by this implementation */
24 #define PS_KEY_USAGE (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT)
25 
26 /* The PSA algorithm used by this implementation */
27 #define PS_CRYPTO_ALG \
28     PSA_ALG_AEAD_WITH_SHORTENED_TAG(PS_CRYPTO_AEAD_ALG, PS_TAG_LEN_BYTES)
29 
30 /*
31  * \brief Check whether the PS AEAD algorithm is a valid one
32  *
33  * Triggers a compilation error if the input algorithm is not a valid AEAD
34  * algorithm. The compilation error should be
35  * "error: 'PS_ERROR_NOT_AEAD_ALG' declared as an array with a negative size"
36  */
37 typedef char PS_ERROR_NOT_AEAD_ALG[(PSA_ALG_IS_AEAD(PS_CRYPTO_ALG)) ? 1 : -1];
38 
39 static psa_key_id_t ps_key;
40 static uint8_t ps_crypto_iv_buf[PS_IV_LEN_BYTES];
41 
ps_crypto_init(void)42 psa_status_t ps_crypto_init(void)
43 {
44     /* For GCM and CCM it is essential that nonce doesn't get repeated. If there
45      * is no rollback protection, an attacker could try to rollback the storage and
46      * encrypt another plaintext block with same IV/Key pair; this breaks GCM and CCM
47      * usage rules.
48      */
49     const psa_algorithm_t ps_crypto_aead_alg = PS_CRYPTO_AEAD_ALG;
50 #ifndef PS_ROLLBACK_PROTECTION
51     if ((ps_crypto_aead_alg == PSA_ALG_GCM) || (ps_crypto_aead_alg == PSA_ALG_CCM)) {
52         return PSA_ERROR_PROGRAMMER_ERROR;
53     }
54 #else
55     (void)ps_crypto_aead_alg;
56 #endif
57     return PSA_SUCCESS;
58 }
59 
ps_crypto_setkey(const uint8_t * key_label,size_t key_label_len)60 psa_status_t ps_crypto_setkey(const uint8_t *key_label, size_t key_label_len)
61 {
62     psa_status_t status;
63     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
64     psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT;
65 
66     if (key_label_len == 0 || key_label == NULL) {
67         return PSA_ERROR_INVALID_ARGUMENT;
68     }
69 
70     /* Set the key attributes for the storage key */
71     psa_set_key_usage_flags(&attributes, PS_KEY_USAGE);
72     psa_set_key_algorithm(&attributes, PS_CRYPTO_ALG);
73     psa_set_key_type(&attributes, PS_KEY_TYPE);
74     psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(PS_KEY_LEN_BYTES));
75 
76     status = psa_key_derivation_setup(&op, PSA_ALG_HKDF(PSA_ALG_SHA_256));
77     if (status != PSA_SUCCESS) {
78         return status;
79     }
80 
81     /* Set up a key derivation operation with HUK  */
82     status = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET,
83                                           TFM_BUILTIN_KEY_ID_HUK);
84     if (status != PSA_SUCCESS) {
85         goto err_release_op;
86     }
87 
88     /* Supply the PS key label as an input to the key derivation */
89     status = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_INFO,
90                                             key_label,
91                                             key_label_len);
92     if (status != PSA_SUCCESS) {
93         goto err_release_op;
94     }
95 
96     /* Create the storage key from the key derivation operation */
97     status = psa_key_derivation_output_key(&attributes, &op, &ps_key);
98     if (status != PSA_SUCCESS) {
99         goto err_release_op;
100     }
101 
102     /* Free resources associated with the key derivation operation */
103     status = psa_key_derivation_abort(&op);
104     if (status != PSA_SUCCESS) {
105         goto err_release_key;
106     }
107 
108     return PSA_SUCCESS;
109 
110 err_release_key:
111     (void)psa_destroy_key(ps_key);
112 
113 err_release_op:
114     (void)psa_key_derivation_abort(&op);
115 
116     return PSA_ERROR_GENERIC_ERROR;
117 }
118 
ps_crypto_destroykey(void)119 psa_status_t ps_crypto_destroykey(void)
120 {
121     psa_status_t status;
122 
123     /* Destroy the transient key */
124     status = psa_destroy_key(ps_key);
125     if (status != PSA_SUCCESS) {
126         return PSA_ERROR_GENERIC_ERROR;
127     }
128 
129     return PSA_SUCCESS;
130 }
131 
ps_crypto_set_iv(const union ps_crypto_t * crypto)132 void ps_crypto_set_iv(const union ps_crypto_t *crypto)
133 {
134     (void)memcpy(ps_crypto_iv_buf, crypto->ref.iv, PS_IV_LEN_BYTES);
135 }
136 
ps_crypto_get_iv(union ps_crypto_t * crypto)137 psa_status_t ps_crypto_get_iv(union ps_crypto_t *crypto)
138 {
139     /* IV characteristic is algorithm dependent.
140      * For GCM it is essential that it doesn't get repeated.
141      * A simple increment will suffice.
142      * FIXME:
143      * Since IV is predictable in this case,
144      * If there is no rollback protection, an attacker could
145      * try to rollback the storage and encrypt another plaintext
146      * block with same IV/Key pair; this breaks GCM usage rules.
147      * One potential fix would be to generate IV through RNG
148      */
149 
150     /* Logic:
151      * IV is a 12 byte value. Read the old value and increment it by 1.
152      * since there is no standard C support for 12 byte integer mathematics,
153      * the increment need to performed manually. Increment the lower 8byte
154      * as uint64_t value and then if the new value is 0, increment the upper
155      * 4 bytes as uint32_t
156      * Endian order doesn't really matter as objective is not to perform
157      * machine accurate increment operation but to generate a non-repetitive
158      * iv value.
159      */
160 
161     uint64_t iv_l;
162     uint32_t iv_h;
163 
164     (void)memcpy(&iv_l, ps_crypto_iv_buf, sizeof(iv_l));
165     (void)memcpy(&iv_h, (ps_crypto_iv_buf+sizeof(iv_l)), sizeof(iv_h));
166     iv_l++;
167     /* If overflow, increment the MSBs */
168     if (iv_l == 0) {
169         iv_h++;
170 
171         /* If overflow, return error. Different IV should be used. */
172         if (iv_h == 0) {
173             /* Reset iv_l and iv_h to the value before increasement. Otherwise,
174              * iv_l will start from '1' the next time this function is called.
175              */
176             iv_l--;
177             iv_h--;
178             return PSA_ERROR_GENERIC_ERROR;
179         }
180     }
181 
182     /* Update the local buffer */
183     (void)memcpy(ps_crypto_iv_buf, &iv_l, sizeof(iv_l));
184     (void)memcpy((ps_crypto_iv_buf + sizeof(iv_l)), &iv_h, sizeof(iv_h));
185     /* Update the caller buffer */
186     (void)memcpy(crypto->ref.iv, ps_crypto_iv_buf, PS_IV_LEN_BYTES);
187 
188     return PSA_SUCCESS;
189 }
190 
ps_crypto_encrypt_and_tag(union ps_crypto_t * crypto,const uint8_t * add,size_t add_len,const uint8_t * in,size_t in_len,uint8_t * out,size_t out_size,size_t * out_len)191 psa_status_t ps_crypto_encrypt_and_tag(union ps_crypto_t *crypto,
192                                        const uint8_t *add,
193                                        size_t add_len,
194                                        const uint8_t *in,
195                                        size_t in_len,
196                                        uint8_t *out,
197                                        size_t out_size,
198                                        size_t *out_len)
199 {
200     psa_status_t status;
201 
202     status = psa_aead_encrypt(ps_key, PS_CRYPTO_ALG,
203                               crypto->ref.iv, PS_IV_LEN_BYTES,
204                               add, add_len,
205                               in, in_len,
206                               out, out_size, out_len);
207     if (status != PSA_SUCCESS) {
208         return PSA_ERROR_GENERIC_ERROR;
209     }
210 
211     /* Copy the tag out of the output buffer */
212     *out_len -= PS_TAG_LEN_BYTES;
213     (void)memcpy(crypto->ref.tag, (out + *out_len), PS_TAG_LEN_BYTES);
214 
215     return PSA_SUCCESS;
216 }
217 
ps_crypto_auth_and_decrypt(const union ps_crypto_t * crypto,const uint8_t * add,size_t add_len,uint8_t * in,size_t in_len,uint8_t * out,size_t out_size,size_t * out_len)218 psa_status_t ps_crypto_auth_and_decrypt(const union ps_crypto_t *crypto,
219                                         const uint8_t *add,
220                                         size_t add_len,
221                                         uint8_t *in,
222                                         size_t in_len,
223                                         uint8_t *out,
224                                         size_t out_size,
225                                         size_t *out_len)
226 {
227     psa_status_t status;
228 
229     /* Copy the tag into the input buffer */
230     (void)memcpy((in + in_len), crypto->ref.tag, PS_TAG_LEN_BYTES);
231     in_len += PS_TAG_LEN_BYTES;
232 
233     status = psa_aead_decrypt(ps_key, PS_CRYPTO_ALG,
234                               crypto->ref.iv, PS_IV_LEN_BYTES,
235                               add, add_len,
236                               in, in_len,
237                               out, out_size, out_len);
238     if (status != PSA_SUCCESS) {
239         return PSA_ERROR_INVALID_SIGNATURE;
240     }
241 
242     return PSA_SUCCESS;
243 }
244 
ps_crypto_generate_auth_tag(union ps_crypto_t * crypto,const uint8_t * add,uint32_t add_len)245 psa_status_t ps_crypto_generate_auth_tag(union ps_crypto_t *crypto,
246                                          const uint8_t *add,
247                                          uint32_t add_len)
248 {
249     psa_status_t status;
250     size_t out_len;
251 
252     status = psa_aead_encrypt(ps_key, PS_CRYPTO_ALG,
253                               crypto->ref.iv, PS_IV_LEN_BYTES,
254                               add, add_len,
255                               0, 0,
256                               crypto->ref.tag, PS_TAG_LEN_BYTES, &out_len);
257     if (status != PSA_SUCCESS || out_len != PS_TAG_LEN_BYTES) {
258         return PSA_ERROR_GENERIC_ERROR;
259     }
260 
261     return PSA_SUCCESS;
262 }
263 
ps_crypto_authenticate(const union ps_crypto_t * crypto,const uint8_t * add,uint32_t add_len)264 psa_status_t ps_crypto_authenticate(const union ps_crypto_t *crypto,
265                                     const uint8_t *add,
266                                     uint32_t add_len)
267 {
268     psa_status_t status;
269     size_t out_len;
270 
271     status = psa_aead_decrypt(ps_key, PS_CRYPTO_ALG,
272                               crypto->ref.iv, PS_IV_LEN_BYTES,
273                               add, add_len,
274                               crypto->ref.tag, PS_TAG_LEN_BYTES,
275                               0, 0, &out_len);
276     if (status != PSA_SUCCESS || out_len != 0) {
277         return PSA_ERROR_INVALID_SIGNATURE;
278     }
279 
280     return PSA_SUCCESS;
281 }
282