Skip to content

Commit da942d8

Browse files
committed
fix: openssl-3.0-fips should use libcrypto HKDF
1 parent 9d02146 commit da942d8

File tree

4 files changed

+139
-19
lines changed

4 files changed

+139
-19
lines changed

crypto/s2n_hkdf.c

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,101 @@ static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm al
175175

176176
return S2N_SUCCESS;
177177
}
178+
179+
bool s2n_libcrypto_supports_hkdf()
180+
{
181+
return true;
182+
}
183+
184+
#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0)
185+
186+
#include "crypto/s2n_kdf.h"
187+
188+
static S2N_RESULT s2n_hkdf_kdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg,
189+
const struct s2n_blob *salt, const struct s2n_blob *key, const struct s2n_blob *info,
190+
struct s2n_blob *output, int mode)
191+
{
192+
/* As an optimization, we should be able to fetch and cache this EVP_KDF*
193+
* once when s2n_init is called.
194+
*/
195+
DEFER_CLEANUP(EVP_KDF *hkdf_impl = EVP_KDF_fetch(NULL, "HKDF", NULL),
196+
EVP_KDF_free_pointer);
197+
RESULT_ENSURE(hkdf_impl, S2N_ERR_PRF_INVALID_ALGORITHM);
198+
199+
DEFER_CLEANUP(EVP_KDF_CTX *hkdf_ctx = EVP_KDF_CTX_new(hkdf_impl),
200+
EVP_KDF_CTX_free_pointer);
201+
RESULT_ENSURE_REF(hkdf_ctx);
202+
203+
const EVP_MD *digest = NULL;
204+
RESULT_GUARD(s2n_hmac_md_from_alg(alg, &digest));
205+
RESULT_ENSURE_REF(digest);
206+
const char *digest_name = EVP_MD_get0_name(digest);
207+
RESULT_ENSURE_REF(digest_name);
208+
209+
OSSL_PARAM params[] = {
210+
S2N_OSSL_PARAM_INT(OSSL_KDF_PARAM_MODE, mode),
211+
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_KEY, key),
212+
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_INFO, info),
213+
S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SALT, salt),
214+
/* Casting away the const is safe because providers are forbidden from
215+
* modifying any OSSL_PARAM value other than return_size.
216+
* Even the examples in the Openssl documentation cast const strings to
217+
* non-const void pointers when setting up OSSL_PARAMs.
218+
*/
219+
S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name),
220+
OSSL_PARAM_END,
221+
};
222+
223+
/* From the HKDF docs (https://docs.openssl.org/3.1/man7/EVP_KDF-HKDF/):
224+
* > When using EVP_KDF_HKDF_MODE_EXTRACT_ONLY the keylen parameter must equal
225+
* > the size of the intermediate fixed-length pseudorandom key otherwise an
226+
* > error will occur.
227+
*/
228+
if (mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) {
229+
RESULT_GUARD_OSSL(EVP_KDF_CTX_set_params(hkdf_ctx, params), S2N_ERR_HKDF);
230+
size_t key_size = EVP_KDF_CTX_get_kdf_size(hkdf_ctx);
231+
RESULT_ENSURE(key_size <= output->size, S2N_ERR_HKDF_OUTPUT_SIZE);
232+
output->size = key_size;
233+
}
234+
235+
RESULT_GUARD_OSSL(EVP_KDF_derive(hkdf_ctx, output->data, output->size, params),
236+
S2N_ERR_HKDF);
237+
return S2N_RESULT_OK;
238+
}
239+
240+
static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg,
241+
const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key)
242+
{
243+
struct s2n_blob empty_info = { 0 };
244+
POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, &empty_info, pseudo_rand_key,
245+
EVP_KDF_HKDF_MODE_EXTRACT_ONLY));
246+
return S2N_SUCCESS;
247+
}
248+
249+
static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg,
250+
const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output)
251+
{
252+
struct s2n_blob empty_salt = { 0 };
253+
POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, &empty_salt, pseudo_rand_key, info, output,
254+
EVP_KDF_HKDF_MODE_EXPAND_ONLY));
255+
return S2N_SUCCESS;
256+
}
257+
258+
static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt,
259+
const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output)
260+
{
261+
POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, info, output,
262+
EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND));
263+
return S2N_SUCCESS;
264+
}
265+
266+
bool s2n_libcrypto_supports_hkdf()
267+
{
268+
return true;
269+
}
270+
178271
#else
272+
179273
static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt,
180274
const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key)
181275
{
@@ -193,6 +287,12 @@ static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm al
193287
{
194288
POSIX_BAIL(S2N_ERR_UNIMPLEMENTED);
195289
}
290+
291+
bool s2n_libcrypto_supports_hkdf()
292+
{
293+
return false;
294+
}
295+
196296
#endif /* S2N_LIBCRYPTO_SUPPORTS_HKDF */
197297

198298
const struct s2n_hkdf_impl s2n_libcrypto_hkdf_impl = {
@@ -290,12 +390,3 @@ int s2n_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s
290390

291391
return S2N_SUCCESS;
292392
}
293-
294-
bool s2n_libcrypto_supports_hkdf()
295-
{
296-
#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF
297-
return true;
298-
#else
299-
return false;
300-
#endif
301-
}

crypto/s2n_kdf.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
#pragma once
17+
18+
#if S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0)
19+
20+
#include <openssl/core_names.h>
21+
#include <openssl/kdf.h>
22+
23+
#define S2N_OSSL_PARAM_BLOB(id, blob) \
24+
OSSL_PARAM_octet_string(id, blob->data, blob->size)
25+
#define S2N_OSSL_PARAM_STR(id, cstr) \
26+
OSSL_PARAM_utf8_string(id, cstr, strlen(cstr))
27+
#define S2N_OSSL_PARAM_INT(id, val) \
28+
OSSL_PARAM_int(id, &val)
29+
30+
DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF_CTX *, EVP_KDF_CTX_free);
31+
DEFINE_POINTER_CLEANUP_FUNC(EVP_KDF *, EVP_KDF_free);
32+
33+
#endif /* Openssl3 only */

crypto/s2n_prf_libcrypto.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,7 @@ S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
8989

9090
#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0)
9191

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);
92+
#include "crypto/s2n_kdf.h"
10293

10394
S2N_RESULT s2n_prf_libcrypto(struct s2n_connection *conn,
10495
struct s2n_blob *secret, struct s2n_blob *label,

tests/unit/s2n_hkdf_test.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ int main(int argc, char **argv)
417417
BEGIN_TEST();
418418
EXPECT_SUCCESS(s2n_disable_tls13_in_test());
419419

420+
/* FIPS always requires libcrypto hkdf */
421+
if (s2n_is_in_fips_mode()) {
422+
EXPECT_TRUE(s2n_libcrypto_supports_hkdf());
423+
}
424+
420425
EXPECT_SUCCESS(s2n_hmac_new(&hmac));
421426

422427
for (uint8_t i = 0; i < NUM_TESTS; i++) {

0 commit comments

Comments
 (0)