|
| 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 <openssl/core_names.h> |
| 93 | + #include <openssl/kdf.h> |
| 94 | + |
| 95 | + #define S2N_OSSL_PARAM_BLOB(id, blob) \ |
| 96 | + OSSL_PARAM_octet_string(id, blob->data, blob->size) |
| 97 | + #define S2N_OSSL_PARAM_STR(id, cstr) \ |
| 98 | + OSSL_PARAM_utf8_string(id, cstr, strlen(cstr)) |
| 99 | + |
| 100 | +DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF_CTX *, EVP_KDF_CTX_free); |
| 101 | +DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF *, EVP_KDF_free); |
| 102 | + |
| 103 | +S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn, |
| 104 | + struct s2n_blob *secret, struct s2n_blob *label, |
| 105 | + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, |
| 106 | + struct s2n_blob *out) |
| 107 | +{ |
| 108 | + RESULT_ENSURE_REF(conn); |
| 109 | + RESULT_ENSURE_REF(secret); |
| 110 | + RESULT_ENSURE_REF(label); |
| 111 | + RESULT_ENSURE_REF(seed_a); |
| 112 | + RESULT_ENSURE_REF(out); |
| 113 | + |
| 114 | + struct s2n_blob empty_seed = { 0 }; |
| 115 | + if (!seed_b) { |
| 116 | + seed_b = &empty_seed; |
| 117 | + } |
| 118 | + if (!seed_c) { |
| 119 | + seed_c = &empty_seed; |
| 120 | + } |
| 121 | + |
| 122 | + /* Openssl limits the size of the seed to 1024 bytes, including the label. |
| 123 | + * This would be an issue for TLS1.2 PQ, which uses full keyshares as seeds. |
| 124 | + * However, s2n-tls doesn't support PQ with Openssl, so this limitation will |
| 125 | + * never affect customers. |
| 126 | + * |
| 127 | + * As of this commit, EVP_KDF_derive will fail silently (without logging any |
| 128 | + * error) if the seed is too large. This check adds visibility. |
| 129 | + */ |
| 130 | + uint64_t seed_total_size = label->size + seed_a->size + seed_b->size + seed_c->size; |
| 131 | + RESULT_ENSURE(seed_total_size <= 1024, S2N_ERR_PRF_INVALID_SEED); |
| 132 | + |
| 133 | + const char *digest_name = "MD5-SHA1"; |
| 134 | + const char *fetch_properties = "-fips"; |
| 135 | + |
| 136 | + if (conn->actual_protocol_version == S2N_TLS12) { |
| 137 | + fetch_properties = ""; |
| 138 | + |
| 139 | + RESULT_ENSURE_REF(conn->secure); |
| 140 | + RESULT_ENSURE_REF(conn->secure->cipher_suite); |
| 141 | + s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; |
| 142 | + |
| 143 | + const EVP_MD *digest = NULL; |
| 144 | + RESULT_GUARD(s2n_hmac_md_from_alg(prf_alg, &digest)); |
| 145 | + RESULT_ENSURE_REF(digest); |
| 146 | + digest_name = EVP_MD_get0_name(digest); |
| 147 | + RESULT_ENSURE_REF(digest_name); |
| 148 | + } |
| 149 | + |
| 150 | + /* As an optimization, we should be able to fetch and cache this EVP_KDF* |
| 151 | + * once when s2n_init is called. |
| 152 | + */ |
| 153 | + DEFER_CLEANUP(EVP_KDF *prf_impl = EVP_KDF_fetch(NULL, "TLS1-PRF", fetch_properties), |
| 154 | + EVP_KDF_free_pointer); |
| 155 | + RESULT_ENSURE(prf_impl, S2N_ERR_PRF_INVALID_ALGORITHM); |
| 156 | + |
| 157 | + DEFER_CLEANUP(EVP_KDF_CTX *prf_ctx = EVP_KDF_CTX_new(prf_impl), |
| 158 | + EVP_KDF_CTX_free_pointer); |
| 159 | + RESULT_ENSURE_REF(prf_ctx); |
| 160 | + |
| 161 | + OSSL_PARAM params[] = { |
| 162 | + /* Casting away the const is safe because providers are forbidden from |
| 163 | + * modifying any OSSL_PARAM value other than return_size. |
| 164 | + * Even the examples in the Openssl documentation cast const strings to |
| 165 | + * non-const void pointers when setting up OSSL_PARAMs. |
| 166 | + */ |
| 167 | + S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_PROPERTIES, (void *) (uintptr_t) fetch_properties), |
| 168 | + S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), |
| 169 | + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SECRET, secret), |
| 170 | + /* "TLS1-PRF" handles the label like just another seed */ |
| 171 | + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, label), |
| 172 | + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_a), |
| 173 | + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_b), |
| 174 | + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SEED, seed_c), |
| 175 | + OSSL_PARAM_END, |
| 176 | + }; |
| 177 | + |
| 178 | + RESULT_GUARD_OSSL(EVP_KDF_derive(prf_ctx, out->data, out->size, params), |
| 179 | + S2N_ERR_PRF_DERIVE); |
| 180 | + return S2N_RESULT_OK; |
| 181 | +} |
| 182 | + |
| 183 | +#else |
| 184 | + |
| 185 | +S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn, |
| 186 | + struct s2n_blob *secret, struct s2n_blob *label, |
| 187 | + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, |
| 188 | + struct s2n_blob *out) |
| 189 | +{ |
| 190 | + RESULT_BAIL(S2N_ERR_FIPS_MODE_UNSUPPORTED); |
| 191 | +} |
| 192 | + |
| 193 | +#endif |
0 commit comments