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