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