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 "crypto/s2n_prf_libcrypto.h"
17 : :
18 : : #include "crypto/s2n_hash.h"
19 : : #include "error/s2n_errno.h"
20 : : #include "tls/s2n_connection.h"
21 : : #include "utils/s2n_safety.h"
22 : :
23 : : #if defined(OPENSSL_IS_AWSLC)
24 : :
25 : : /* The AWSLC TLS PRF API is exported in all AWSLC versions. However, in the AWSLC FIPS branch, this
26 : : * API is defined in a private header:
27 : : * https://github.com/aws/aws-lc/blob/d251b365b73a6e6acff6ee634aa8f077f23cdea4/crypto/fipsmodule/tls/internal.h#L27
28 : : *
29 : : * AWSLC has committed to this API definition, and the API has been added to a public header in the
30 : : * main branch: https://github.com/aws/aws-lc/pull/1033. As such, this API is forward-declared in
31 : : * order to make it accessible to s2n-tls when linked to AWSLC-FIPS.
32 : : */
33 : : int CRYPTO_tls1_prf(const EVP_MD *digest,
34 : : uint8_t *out, size_t out_len,
35 : : const uint8_t *secret, size_t secret_len,
36 : : const char *label, size_t label_len,
37 : : const uint8_t *seed1, size_t seed1_len,
38 : : const uint8_t *seed2, size_t seed2_len);
39 : :
40 : : S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
41 : : struct s2n_blob *secret, struct s2n_blob *label,
42 : : struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c,
43 : : struct s2n_blob *out)
44 : : {
45 : : const EVP_MD *digest = NULL;
46 : : if (conn->actual_protocol_version < S2N_TLS12) {
47 : : /* md5_sha1 is a digest that indicates both MD5 and SHA1 should be used in the PRF calculation.
48 : : * This is needed for pre-TLS12 PRFs.
49 : : */
50 : : digest = EVP_md5_sha1();
51 : : } else {
52 : : RESULT_GUARD(s2n_hmac_md_from_alg(conn->secure->cipher_suite->prf_alg, &digest));
53 : : }
54 : : RESULT_ENSURE_REF(digest);
55 : :
56 : : DEFER_CLEANUP(struct s2n_stuffer seed_b_stuffer = { 0 }, s2n_stuffer_free);
57 : : size_t seed_b_len = 0;
58 : : uint8_t *seed_b_data = NULL;
59 : :
60 : : if (seed_b != NULL) {
61 : : struct s2n_blob seed_b_blob = { 0 };
62 : : RESULT_GUARD_POSIX(s2n_blob_init(&seed_b_blob, seed_b->data, seed_b->size));
63 : : RESULT_GUARD_POSIX(s2n_stuffer_init_written(&seed_b_stuffer, &seed_b_blob));
64 : :
65 : : if (seed_c != NULL) {
66 : : /* The AWSLC TLS PRF implementation only provides two seed arguments. If three seeds
67 : : * were provided, pass in the third seed by concatenating it with the second seed.
68 : : */
69 : : RESULT_GUARD_POSIX(s2n_stuffer_alloc(&seed_b_stuffer, seed_b->size + seed_c->size));
70 : : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_b->data, seed_b->size));
71 : : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&seed_b_stuffer, seed_c->data, seed_c->size));
72 : : }
73 : :
74 : : seed_b_len = s2n_stuffer_data_available(&seed_b_stuffer);
75 : : seed_b_data = s2n_stuffer_raw_read(&seed_b_stuffer, seed_b_len);
76 : : RESULT_ENSURE_REF(seed_b_data);
77 : : }
78 : :
79 : : RESULT_GUARD_OSSL(CRYPTO_tls1_prf(digest,
80 : : out->data, out->size,
81 : : secret->data, secret->size,
82 : : (const char *) label->data, label->size,
83 : : seed_a->data, seed_a->size,
84 : : seed_b_data, seed_b_len),
85 : : S2N_ERR_PRF_DERIVE);
86 : :
87 : : return S2N_RESULT_OK;
88 : : }
89 : :
90 : : #elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0)
91 : :
92 : : #include "crypto/s2n_kdf.h"
93 : :
94 : : S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
95 : : struct s2n_blob *secret, struct s2n_blob *label,
96 : : struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c,
97 : : struct s2n_blob *out)
98 : 0 : {
99 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(conn);
100 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(secret);
101 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(label);
102 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(seed_a);
103 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(out);
104 : :
105 : 0 : struct s2n_blob empty_seed = { 0 };
106 [ # # ]: 0 : if (!seed_b) {
107 : 0 : seed_b = &empty_seed;
108 : 0 : }
109 [ # # ]: 0 : if (!seed_c) {
110 : 0 : seed_c = &empty_seed;
111 : 0 : }
112 : :
113 : : /* Openssl limits the size of the seed to 1024 bytes, including the label.
114 : : * This would be an issue for TLS1.2 PQ, which uses full keyshares as seeds.
115 : : * However, s2n-tls doesn't support PQ with Openssl, so this limitation will
116 : : * never affect customers.
117 : : *
118 : : * As of this commit, EVP_KDF_derive will fail silently (without logging any
119 : : * error) if the seed is too large. This check adds visibility.
120 : : */
121 : 0 : uint64_t seed_total_size = label->size + seed_a->size + seed_b->size + seed_c->size;
122 [ # # ][ # # ]: 0 : RESULT_ENSURE(seed_total_size <= 1024, S2N_ERR_PRF_INVALID_SEED);
123 : :
124 : 0 : const char *digest_name = "MD5-SHA1";
125 : 0 : const char *fetch_properties = "-fips";
126 : :
127 [ # # ]: 0 : if (conn->actual_protocol_version == S2N_TLS12) {
128 : 0 : fetch_properties = "";
129 : :
130 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(conn->secure);
131 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(conn->secure->cipher_suite);
132 : 0 : s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg;
133 : :
134 : 0 : const EVP_MD *digest = NULL;
135 [ # # ]: 0 : RESULT_GUARD(s2n_hmac_md_from_alg(prf_alg, &digest));
136 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(digest);
137 : 0 : digest_name = EVP_MD_get0_name(digest);
138 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(digest_name);
139 : 0 : }
140 : :
141 : : /* As an optimization, we should be able to fetch and cache this EVP_KDF*
142 : : * once when s2n_init is called.
143 : : */
144 : 0 : DEFER_CLEANUP(EVP_KDF *prf_impl = EVP_KDF_fetch(NULL, "TLS1-PRF", fetch_properties),
145 : 0 : EVP_KDF_free_pointer);
146 [ # # ][ # # ]: 0 : RESULT_ENSURE(prf_impl, S2N_ERR_PRF_INVALID_ALGORITHM);
147 : :
148 : 0 : DEFER_CLEANUP(EVP_KDF_CTX *prf_ctx = EVP_KDF_CTX_new(prf_impl),
149 : 0 : EVP_KDF_CTX_free_pointer);
150 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(prf_ctx);
151 : :
152 : 0 : OSSL_PARAM params[] = {
153 : : /* Casting away the const is safe because providers are forbidden from
154 : : * modifying any OSSL_PARAM value other than return_size.
155 : : * Even the examples in the Openssl documentation cast const strings to
156 : : * non-const void pointers when setting up OSSL_PARAMs.
157 : : */
158 : 0 : S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_PROPERTIES, (void *) (uintptr_t) fetch_properties),
159 : 0 : S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name),
160 : 0 : S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SECRET, secret),
161 : : /* "TLS1-PRF" handles the label like just another seed */
162 : 0 : S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, label),
163 : 0 : S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_a),
164 : 0 : S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_b),
165 : 0 : S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_c),
166 : 0 : OSSL_PARAM_END,
167 : 0 : };
168 : :
169 [ # # ][ # # ]: 0 : RESULT_GUARD_OSSL(EVP_KDF_derive(prf_ctx, out->data, out->size, params),
170 : 0 : S2N_ERR_PRF_DERIVE);
171 : 0 : return S2N_RESULT_OK;
172 : 0 : }
173 : :
174 : : #else
175 : :
176 : : S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
177 : : struct s2n_blob *secret, struct s2n_blob *label,
178 : : struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c,
179 : : struct s2n_blob *out)
180 : : {
181 : : RESULT_BAIL(S2N_ERR_FIPS_MODE_UNSUPPORTED);
182 : : }
183 : :
184 : : #endif
|