Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #include <openssl/evp.h>
17 : :
18 : : #include "crypto/s2n_cipher.h"
19 : : #include "crypto/s2n_libcrypto.h"
20 : : #include "crypto/s2n_openssl.h"
21 : : #include "tls/s2n_crypto.h"
22 : : #include "utils/s2n_blob.h"
23 : : #include "utils/s2n_safety.h"
24 : :
25 : : /* We support two different backing implementations of ChaCha20-Poly1305: one
26 : : * implementation for OpenSSL (>= 1.1.0, see
27 : : * https://www.openssl.org/news/cl110.txt) and one implementation for BoringSSL
28 : : * and AWS-LC. LibreSSL supports ChaCha20-Poly1305, but the interface is
29 : : * different.
30 : : * Note, the order in the if/elif below matters because both BoringSSL and
31 : : * AWS-LC define OPENSSL_VERSION_NUMBER. */
32 : : #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
33 : : #define S2N_CHACHA20_POLY1305_AVAILABLE_BSSL_AWSLC
34 : : #elif (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) && !defined(LIBRESSL_VERSION_NUMBER))
35 : : #define S2N_CHACHA20_POLY1305_AVAILABLE_OSSL
36 : : #endif
37 : :
38 : : static bool s2n_aead_chacha20_poly1305_available(void)
39 : 2187 : {
40 : 2187 : #if defined(S2N_CHACHA20_POLY1305_AVAILABLE_OSSL) || defined(S2N_CHACHA20_POLY1305_AVAILABLE_BSSL_AWSLC)
41 : : /* We could support ChaChaPoly with openssl-3.0-fips,
42 : : * but it would require more branching and logic to fetch a non-fips EVP_CIPHER.
43 : : * For now, just consider ChaChaPoly unsupported by openssl-3.0-fips.
44 : : */
45 : 2187 : return !s2n_libcrypto_is_openssl_fips();
46 : : #else
47 : : return false;
48 : : #endif
49 : 2187 : }
50 : :
51 : : #if defined(S2N_CHACHA20_POLY1305_AVAILABLE_OSSL) /* OpenSSL implementation */
52 : :
53 : : static int s2n_aead_chacha20_poly1305_encrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
54 : 1057915 : {
55 [ - + ][ # # ]: 1057915 : POSIX_ENSURE_GTE(in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
56 : : /* The size of the |in| blob includes the size of the data and the size of the ChaCha20-Poly1305 tag */
57 [ # # ][ - + ]: 1057915 : POSIX_ENSURE_GTE(out->size, in->size);
58 [ # # ][ - + ]: 1057915 : POSIX_ENSURE_EQ(iv->size, S2N_TLS_CHACHA20_POLY1305_IV_LEN);
59 : :
60 : : /* Initialize the IV */
61 [ - + ][ # # ]: 1057915 : POSIX_GUARD_OSSL(EVP_EncryptInit_ex(key->evp_cipher_ctx, NULL, NULL, NULL, iv->data), S2N_ERR_KEY_INIT);
62 : :
63 : : /* Adjust input length and buffer pointer to account for the Tag length */
64 : 1057915 : int in_len = in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN;
65 : 1057915 : uint8_t *tag_data = out->data + out->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN;
66 : :
67 : : /* out_len is set by EVP_EncryptUpdate and checked post operation */
68 : 1057915 : int out_len = 0;
69 : : /* Specify the AAD */
70 [ - + ][ # # ]: 1057915 : POSIX_GUARD_OSSL(EVP_EncryptUpdate(key->evp_cipher_ctx, NULL, &out_len, aad->data, aad->size), S2N_ERR_ENCRYPT);
71 : :
72 : : /* Encrypt the data */
73 [ - + ][ # # ]: 1057915 : POSIX_GUARD_OSSL(EVP_EncryptUpdate(key->evp_cipher_ctx, out->data, &out_len, in->data, in_len), S2N_ERR_ENCRYPT);
74 : :
75 : : /* For OpenSSL 1.1.0 and 1.1.1, when using ChaCha20-Poly1305, *out_len is the number of bytes written by EVP_EncryptUpdate. Since the tag is not written during this call, we do not take S2N_TLS_CHACHA20_POLY1305_TAG_LEN into account */
76 [ - + ][ # # ]: 1057915 : S2N_ERROR_IF(in_len != out_len, S2N_ERR_ENCRYPT);
77 : :
78 : : /* Finalize */
79 [ # # ][ - + ]: 1057915 : POSIX_GUARD_OSSL(EVP_EncryptFinal_ex(key->evp_cipher_ctx, out->data, &out_len), S2N_ERR_ENCRYPT);
80 : :
81 : : /* Write the tag */
82 [ - + ][ # # ]: 1057915 : POSIX_GUARD_OSSL(EVP_CIPHER_CTX_ctrl(key->evp_cipher_ctx, EVP_CTRL_AEAD_GET_TAG, S2N_TLS_CHACHA20_POLY1305_TAG_LEN, tag_data), S2N_ERR_ENCRYPT);
83 : :
84 : : /* For OpenSSL 1.1.0 and 1.1.1, when using ChaCha20-Poly1305, EVP_EncryptFinal_ex does not write any bytes. So, we should expect *out_len = 0. */
85 [ - + ][ # # ]: 1057915 : S2N_ERROR_IF(0 != out_len, S2N_ERR_ENCRYPT);
86 : :
87 : 1057915 : return 0;
88 : 1057915 : }
89 : :
90 : : static int s2n_aead_chacha20_poly1305_decrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
91 : 1057907 : {
92 [ # # ][ - + ]: 1057907 : POSIX_ENSURE_GTE(in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
93 [ - + ][ # # ]: 1057907 : POSIX_ENSURE_GTE(out->size, in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
94 [ - + ][ # # ]: 1057907 : POSIX_ENSURE_EQ(iv->size, S2N_TLS_CHACHA20_POLY1305_IV_LEN);
95 : :
96 : : /* Initialize the IV */
97 [ # # ][ - + ]: 1057907 : POSIX_GUARD_OSSL(EVP_DecryptInit_ex(key->evp_cipher_ctx, NULL, NULL, NULL, iv->data), S2N_ERR_KEY_INIT);
98 : :
99 : : /* Adjust input length and buffer pointer to account for the Tag length */
100 : 1057907 : int in_len = in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN;
101 : 1057907 : uint8_t *tag_data = in->data + in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN;
102 : :
103 : : /* Set the TAG */
104 [ - + ][ # # ]: 1057907 : POSIX_GUARD_OSSL(EVP_CIPHER_CTX_ctrl(key->evp_cipher_ctx, EVP_CTRL_GCM_SET_TAG, S2N_TLS_CHACHA20_POLY1305_TAG_LEN, tag_data), S2N_ERR_DECRYPT);
105 : :
106 : : /* out_len is set by EVP_DecryptUpdate. While we verify the content of out_len in
107 : : * s2n_aead_chacha20_poly1305_encrypt, we refrain from this here. This is to avoid
108 : : * doing any branching before the ciphertext is verified. */
109 : 1057907 : int out_len = 0;
110 : : /* Specify the AAD */
111 [ - + ][ # # ]: 1057907 : POSIX_GUARD_OSSL(EVP_DecryptUpdate(key->evp_cipher_ctx, NULL, &out_len, aad->data, aad->size), S2N_ERR_DECRYPT);
112 : :
113 : 1057907 : int evp_decrypt_rc = 1;
114 : : /* Decrypt the data, but don't short circuit tag verification. EVP_Decrypt* return 0 on failure, 1 for success. */
115 : 1057907 : evp_decrypt_rc &= EVP_DecryptUpdate(key->evp_cipher_ctx, out->data, &out_len, in->data, in_len);
116 : :
117 : : /* Verify the tag */
118 : 1057907 : evp_decrypt_rc &= EVP_DecryptFinal_ex(key->evp_cipher_ctx, out->data, &out_len);
119 : :
120 [ + + ][ + - ]: 1057907 : S2N_ERROR_IF(evp_decrypt_rc != 1, S2N_ERR_DECRYPT);
121 : :
122 : 1712 : return 0;
123 : 1057907 : }
124 : :
125 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_encryption_key(struct s2n_session_key *key, struct s2n_blob *in)
126 : 1057981 : {
127 [ - + ][ # # ]: 1057981 : RESULT_ENSURE_EQ(in->size, S2N_TLS_CHACHA20_POLY1305_KEY_LEN);
128 : :
129 [ # # ][ - + ]: 1057981 : RESULT_GUARD_OSSL(EVP_EncryptInit_ex(key->evp_cipher_ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL), S2N_ERR_KEY_INIT);
130 : :
131 : 1057981 : EVP_CIPHER_CTX_ctrl(key->evp_cipher_ctx, EVP_CTRL_AEAD_SET_IVLEN, S2N_TLS_CHACHA20_POLY1305_IV_LEN, NULL);
132 : :
133 [ - + ][ # # ]: 1057981 : RESULT_GUARD_OSSL(EVP_EncryptInit_ex(key->evp_cipher_ctx, NULL, NULL, in->data, NULL), S2N_ERR_KEY_INIT);
134 : :
135 : 1057981 : return S2N_RESULT_OK;
136 : 1057981 : }
137 : :
138 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_decryption_key(struct s2n_session_key *key, struct s2n_blob *in)
139 : 1057956 : {
140 [ - + ][ # # ]: 1057956 : RESULT_ENSURE_EQ(in->size, S2N_TLS_CHACHA20_POLY1305_KEY_LEN);
141 : :
142 [ # # ][ - + ]: 1057956 : RESULT_GUARD_OSSL(EVP_DecryptInit_ex(key->evp_cipher_ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL), S2N_ERR_KEY_INIT);
143 : :
144 : 1057956 : EVP_CIPHER_CTX_ctrl(key->evp_cipher_ctx, EVP_CTRL_AEAD_SET_IVLEN, S2N_TLS_CHACHA20_POLY1305_IV_LEN, NULL);
145 : :
146 [ # # ][ - + ]: 1057956 : RESULT_GUARD_OSSL(EVP_DecryptInit_ex(key->evp_cipher_ctx, NULL, NULL, in->data, NULL), S2N_ERR_KEY_INIT);
147 : :
148 : 1057956 : return S2N_RESULT_OK;
149 : 1057956 : }
150 : :
151 : : static S2N_RESULT s2n_aead_chacha20_poly1305_init(struct s2n_session_key *key)
152 : 2115426 : {
153 [ # # ][ - + ]: 2115426 : RESULT_EVP_CTX_INIT(key->evp_cipher_ctx);
154 : :
155 : 2115426 : return S2N_RESULT_OK;
156 : 2115426 : }
157 : :
158 : : static S2N_RESULT s2n_aead_chacha20_poly1305_destroy_key(struct s2n_session_key *key)
159 : 8458198 : {
160 : 8458198 : EVP_CIPHER_CTX_cleanup(key->evp_cipher_ctx);
161 : :
162 : 8458198 : return S2N_RESULT_OK;
163 : 8458198 : }
164 : :
165 : : #elif defined(S2N_CHACHA20_POLY1305_AVAILABLE_BSSL_AWSLC) /* BoringSSL and AWS-LC implementation */
166 : :
167 : : static int s2n_aead_chacha20_poly1305_encrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
168 : : {
169 : : POSIX_ENSURE_GTE(in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
170 : : /* The size of the |in| blob includes the size of the data and the size of the ChaCha20-Poly1305 tag */
171 : : POSIX_ENSURE_GTE(out->size, in->size);
172 : : POSIX_ENSURE_EQ(iv->size, S2N_TLS_CHACHA20_POLY1305_IV_LEN);
173 : :
174 : : /* Adjust input length to account for the Tag length */
175 : : size_t in_len = in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN;
176 : : /* out_len is set by EVP_AEAD_CTX_seal and checked post operation */
177 : : size_t out_len = 0;
178 : :
179 : : POSIX_GUARD_OSSL(EVP_AEAD_CTX_seal(key->evp_aead_ctx, out->data, &out_len, out->size, iv->data, iv->size, in->data, in_len, aad->data, aad->size), S2N_ERR_ENCRYPT);
180 : :
181 : : S2N_ERROR_IF((in_len + S2N_TLS_CHACHA20_POLY1305_TAG_LEN) != out_len, S2N_ERR_ENCRYPT);
182 : :
183 : : return 0;
184 : : }
185 : :
186 : : static int s2n_aead_chacha20_poly1305_decrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
187 : : {
188 : : POSIX_ENSURE_GTE(in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
189 : : POSIX_ENSURE_GTE(out->size, in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN);
190 : : POSIX_ENSURE_EQ(iv->size, S2N_TLS_CHACHA20_POLY1305_IV_LEN);
191 : :
192 : : /* out_len is set by EVP_AEAD_CTX_open and checked post operation */
193 : : size_t out_len = 0;
194 : :
195 : : POSIX_GUARD_OSSL(EVP_AEAD_CTX_open(key->evp_aead_ctx, out->data, &out_len, out->size, iv->data, iv->size, in->data, in->size, aad->data, aad->size), S2N_ERR_DECRYPT);
196 : :
197 : : S2N_ERROR_IF((in->size - S2N_TLS_CHACHA20_POLY1305_TAG_LEN) != out_len, S2N_ERR_ENCRYPT);
198 : :
199 : : return 0;
200 : : }
201 : :
202 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_encryption_key(struct s2n_session_key *key, struct s2n_blob *in)
203 : : {
204 : : RESULT_ENSURE_EQ(in->size, S2N_TLS_CHACHA20_POLY1305_KEY_LEN);
205 : :
206 : : RESULT_GUARD_OSSL(EVP_AEAD_CTX_init(key->evp_aead_ctx, EVP_aead_chacha20_poly1305(), in->data, in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN, NULL), S2N_ERR_KEY_INIT);
207 : :
208 : : return S2N_RESULT_OK;
209 : : }
210 : :
211 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_decryption_key(struct s2n_session_key *key, struct s2n_blob *in)
212 : : {
213 : : RESULT_ENSURE_EQ(in->size, S2N_TLS_CHACHA20_POLY1305_KEY_LEN);
214 : :
215 : : RESULT_GUARD_OSSL(EVP_AEAD_CTX_init(key->evp_aead_ctx, EVP_aead_chacha20_poly1305(), in->data, in->size, S2N_TLS_CHACHA20_POLY1305_TAG_LEN, NULL), S2N_ERR_KEY_INIT);
216 : :
217 : : return S2N_RESULT_OK;
218 : : }
219 : :
220 : : static S2N_RESULT s2n_aead_chacha20_poly1305_init(struct s2n_session_key *key)
221 : : {
222 : : EVP_AEAD_CTX_zero(key->evp_aead_ctx);
223 : :
224 : : return S2N_RESULT_OK;
225 : : }
226 : :
227 : : static S2N_RESULT s2n_aead_chacha20_poly1305_destroy_key(struct s2n_session_key *key)
228 : : {
229 : : EVP_AEAD_CTX_cleanup(key->evp_aead_ctx);
230 : :
231 : : return S2N_RESULT_OK;
232 : : }
233 : :
234 : : #else /* No ChaCha20-Poly1305 implementation exists for chosen cryptographic provider (E.g Openssl 1.0.x) */
235 : :
236 : : static int s2n_aead_chacha20_poly1305_encrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
237 : : {
238 : : POSIX_BAIL(S2N_ERR_ENCRYPT);
239 : : }
240 : :
241 : : static int s2n_aead_chacha20_poly1305_decrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *aad, struct s2n_blob *in, struct s2n_blob *out)
242 : : {
243 : : POSIX_BAIL(S2N_ERR_DECRYPT);
244 : : }
245 : :
246 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_encryption_key(struct s2n_session_key *key, struct s2n_blob *in)
247 : : {
248 : : RESULT_BAIL(S2N_ERR_KEY_INIT);
249 : : }
250 : :
251 : : static S2N_RESULT s2n_aead_chacha20_poly1305_set_decryption_key(struct s2n_session_key *key, struct s2n_blob *in)
252 : : {
253 : : RESULT_BAIL(S2N_ERR_KEY_INIT);
254 : : }
255 : :
256 : : static S2N_RESULT s2n_aead_chacha20_poly1305_init(struct s2n_session_key *key)
257 : : {
258 : : RESULT_BAIL(S2N_ERR_KEY_INIT);
259 : : }
260 : :
261 : : static S2N_RESULT s2n_aead_chacha20_poly1305_destroy_key(struct s2n_session_key *key)
262 : : {
263 : : RESULT_BAIL(S2N_ERR_KEY_DESTROY);
264 : : }
265 : :
266 : : #endif
267 : :
268 : : const struct s2n_cipher s2n_chacha20_poly1305 = {
269 : : .key_material_size = S2N_TLS_CHACHA20_POLY1305_KEY_LEN,
270 : : .type = S2N_AEAD,
271 : : .io.aead = {
272 : : .record_iv_size = S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN,
273 : : .fixed_iv_size = S2N_TLS_CHACHA20_POLY1305_FIXED_IV_LEN,
274 : : .tag_size = S2N_TLS_CHACHA20_POLY1305_TAG_LEN,
275 : : .decrypt = s2n_aead_chacha20_poly1305_decrypt,
276 : : .encrypt = s2n_aead_chacha20_poly1305_encrypt },
277 : : .is_available = s2n_aead_chacha20_poly1305_available,
278 : : .init = s2n_aead_chacha20_poly1305_init,
279 : : .set_encryption_key = s2n_aead_chacha20_poly1305_set_encryption_key,
280 : : .set_decryption_key = s2n_aead_chacha20_poly1305_set_decryption_key,
281 : : .destroy_key = s2n_aead_chacha20_poly1305_destroy_key,
282 : : };
|