1 /*
2  * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "ps_encrypted_object.h"
9 
10 #include <stddef.h>
11 #include <string.h>
12 
13 #include "crypto/ps_crypto_interface.h"
14 #include "psa/internal_trusted_storage.h"
15 #include "ps_object_defs.h"
16 #include "ps_utils.h"
17 
18 /* Gets the size of data to encrypt */
19 #define PS_ENCRYPT_SIZE(plaintext_size) \
20     ((plaintext_size) + PS_OBJECT_HEADER_SIZE - sizeof(union ps_crypto_t))
21 
22 #define PS_OBJECT_START_POSITION  0
23 
24 /* Buffer to store the maximum encrypted object */
25 /* FIXME: Do partial encrypt/decrypt to reduce the size of internal buffer */
26 #define PS_MAX_ENCRYPTED_OBJ_SIZE PS_ENCRYPT_SIZE(PS_MAX_OBJECT_DATA_SIZE)
27 
28 /* FIXME: add the tag length to the crypto buffer size to account for the tag
29  * being appended to the ciphertext by the crypto layer.
30  */
31 #define PS_TAG_IV_LEN_MAX   ((PS_TAG_LEN_BYTES > PS_IV_LEN_BYTES) ? \
32                              PS_TAG_LEN_BYTES : PS_IV_LEN_BYTES)
33 #define PS_CRYPTO_BUF_LEN (PS_MAX_ENCRYPTED_OBJ_SIZE + PS_TAG_IV_LEN_MAX)
34 
35 static uint8_t ps_crypto_buf[PS_CRYPTO_BUF_LEN];
36 
fill_key_label(struct ps_object_t * obj,size_t * length)37 static psa_status_t fill_key_label(struct ps_object_t *obj, size_t *length)
38 {
39     psa_storage_uid_t uid = obj->header.crypto.ref.uid;
40     int32_t client_id = obj->header.crypto.ref.client_id;
41 
42     if (PS_CRYPTO_BUF_LEN < (sizeof(client_id) + sizeof(uid))) {
43         return PSA_ERROR_BUFFER_TOO_SMALL;
44     }
45 
46     memcpy(ps_crypto_buf, &client_id, sizeof(client_id));
47     memcpy(ps_crypto_buf + sizeof(client_id), &uid, sizeof(uid));
48 
49     *length = sizeof(client_id) + sizeof(uid);
50 
51     return PSA_SUCCESS;
52 }
53 
54 /**
55  * \brief Performs authenticated decryption on object data, with the header as
56  *        the associated data.
57  *
58  * \param[in]  fid       File ID
59  * \param[in]  cur_size  Size of the object data to decrypt
60  * \param[in,out] obj    Pointer to the object structure to authenticate and
61  *                       fill in with the decrypted data. The tag of the object
62  *                       is the one stored in the object table for the given
63  *                       File ID.
64  *
65  * \return Returns error code as specified in \ref psa_status_t
66  */
ps_object_auth_decrypt(uint32_t fid,uint32_t cur_size,struct ps_object_t * obj)67 static psa_status_t ps_object_auth_decrypt(uint32_t fid,
68                                            uint32_t cur_size,
69                                            struct ps_object_t *obj)
70 {
71     psa_status_t err;
72     uint8_t *p_obj_data = (uint8_t *)&obj->header.info;
73     size_t out_len, label_length;
74 
75     err = fill_key_label(obj, &label_length);
76     if (err != PSA_SUCCESS) {
77         return err;
78     }
79 
80     err = ps_crypto_setkey(ps_crypto_buf, label_length);
81     if (err != PSA_SUCCESS) {
82         return err;
83     }
84 
85     (void)memcpy(ps_crypto_buf, p_obj_data, cur_size);
86 
87     /* Use File ID as a part of the associated data to authenticate
88      * the object in the FS. The tag will be stored in the object table and
89      * not as a part of the object's data stored in the FS.
90      */
91 
92     err = ps_crypto_auth_and_decrypt(&obj->header.crypto,
93                                      (const uint8_t *)&fid,
94                                      sizeof(fid),
95                                      ps_crypto_buf,
96                                      cur_size,
97                                      p_obj_data,
98                                      sizeof(*obj) - sizeof(obj->header.crypto),
99                                      &out_len);
100     if (err != PSA_SUCCESS || out_len != cur_size) {
101         (void)ps_crypto_destroykey();
102         return PSA_ERROR_GENERIC_ERROR;
103     }
104 
105     return ps_crypto_destroykey();
106 }
107 
108 /**
109  * \brief Performs authenticated encryption on object data, with the header as
110  *        the associated data.
111  *
112  * \param[in]  fid       File ID
113  * \param[in]  cur_size  Size of the object data to encrypt
114  * \param[out] obj       Pointer to the object structure to authenticate and
115  *                       fill in with the encrypted data.
116  *
117  * \return Returns error code as specified in \ref psa_status_t
118  */
ps_object_auth_encrypt(uint32_t fid,uint32_t cur_size,struct ps_object_t * obj)119 static psa_status_t ps_object_auth_encrypt(uint32_t fid,
120                                            uint32_t cur_size,
121                                            struct ps_object_t *obj)
122 {
123     psa_status_t err;
124     uint8_t *p_obj_data = (uint8_t *)&obj->header.info;
125     size_t out_len, label_length;
126 
127     err = fill_key_label(obj, &label_length);
128     if (err != PSA_SUCCESS) {
129         return err;
130     }
131 
132     err = ps_crypto_setkey(ps_crypto_buf, label_length);
133     if (err != PSA_SUCCESS) {
134         return err;
135     }
136 
137     /* Get a new IV for each encryption */
138     err = ps_crypto_get_iv(&obj->header.crypto);
139     if (err != PSA_SUCCESS) {
140         return err;
141     }
142 
143     /* Use File ID as a part of the associated data to authenticate
144      * the object in the FS. The tag will be stored in the object table and
145      * not as a part of the object's data stored in the FS.
146      */
147 
148     err = ps_crypto_encrypt_and_tag(&obj->header.crypto,
149                                     (const uint8_t *)&fid,
150                                     sizeof(fid),
151                                     p_obj_data,
152                                     cur_size,
153                                     ps_crypto_buf,
154                                     sizeof(ps_crypto_buf),
155                                     &out_len);
156     if (err != PSA_SUCCESS || out_len != cur_size) {
157         (void)ps_crypto_destroykey();
158         return PSA_ERROR_GENERIC_ERROR;
159     }
160 
161     (void)memcpy(p_obj_data, ps_crypto_buf, cur_size);
162 
163     return ps_crypto_destroykey();
164 }
165 
ps_encrypted_object_read(uint32_t fid,struct ps_object_t * obj)166 psa_status_t ps_encrypted_object_read(uint32_t fid, struct ps_object_t *obj)
167 {
168     psa_status_t err;
169     uint32_t decrypt_size;
170     size_t data_length;
171 
172     /* Read the encrypted object from the persistent area. The data stored via
173      * ITS interface of this `fid` is the encrypted object together with the
174      * `IV`.
175      * In the psa_its_get, the buffer size is not checked. Check the buffer size
176      * here.
177      */
178     if (sizeof(ps_crypto_buf) < PS_MAX_ENCRYPTED_OBJ_SIZE + PS_IV_LEN_BYTES) {
179         return PSA_ERROR_GENERIC_ERROR;
180     }
181     err = psa_its_get(fid, PS_OBJECT_START_POSITION,
182                       PS_MAX_ENCRYPTED_OBJ_SIZE + PS_IV_LEN_BYTES,
183                       (void *)ps_crypto_buf,
184                       &data_length);
185     if (err != PSA_SUCCESS) {
186         return err;
187     }
188 
189     /* Get the decrypt size. IV is also stored by ITS service. It is at the end
190      * of the read out data. Toolchains may add padding byte after iv array in
191      * crypto.ref structure. Separate the copies of header.info and iv array to
192      * skip the padding byte.
193      */
194     decrypt_size = data_length - sizeof(obj->header.crypto.ref.iv);
195     memcpy(&obj->header.info, ps_crypto_buf, decrypt_size);
196     memcpy(obj->header.crypto.ref.iv,
197            ps_crypto_buf + decrypt_size,
198            sizeof(obj->header.crypto.ref.iv));
199 
200     /* Decrypt the object data */
201     err = ps_object_auth_decrypt(fid, decrypt_size, obj);
202     if (err != PSA_SUCCESS) {
203         return err;
204     }
205 
206     return PSA_SUCCESS;
207 }
208 
ps_encrypted_object_write(uint32_t fid,struct ps_object_t * obj)209 psa_status_t ps_encrypted_object_write(uint32_t fid, struct ps_object_t *obj)
210 {
211     psa_status_t err;
212     uint32_t wrt_size;
213 
214     wrt_size = PS_ENCRYPT_SIZE(obj->header.info.current_size);
215 
216     /* Authenticate and encrypt the object */
217     err = ps_object_auth_encrypt(fid, wrt_size, obj);
218     if (err != PSA_SUCCESS) {
219         return err;
220     }
221 
222     /* The IV will also be stored. The encrypted data is stored in ps_crypto_buf
223      * now. Append the value of the 'iv' to the end of the encrypted data.
224      * Toolchains may add padding byte after iv array in crypto.ref structure.
225      * The padding byte shall not be written into the storage area.
226      */
227     (void)memcpy(ps_crypto_buf + wrt_size,
228                      obj->header.crypto.ref.iv,
229                      sizeof(obj->header.crypto.ref.iv));
230     wrt_size += sizeof(obj->header.crypto.ref.iv);
231 
232     /* Write the encrypted object to the persistent area. The tag values is not
233      * copied as it is stored in the object table.
234      */
235     return psa_its_set(fid, wrt_size, (const void *)ps_crypto_buf,
236                        PSA_STORAGE_FLAG_NONE);
237 }
238