// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC #include #include #include #include #include #include "../delocate.h" #include "internal.h" static int sskdf_variant_digest_ctx_init(sskdf_variant_ctx *ctx, const EVP_MD *digest) { sskdf_variant_digest_ctx *variant_ctx = NULL; EVP_MD_CTX *md_ctx = NULL; int ret = 0; if (!ctx || ctx->data || !digest) { goto err; } variant_ctx = OPENSSL_malloc(sizeof(sskdf_variant_digest_ctx)); if (!variant_ctx) { goto err; } md_ctx = EVP_MD_CTX_new(); if (!md_ctx) { goto err; } ret = 1; variant_ctx->digest = digest; variant_ctx->md_ctx = md_ctx; ctx->data = variant_ctx; return ret; err: EVP_MD_CTX_free(md_ctx); OPENSSL_free(variant_ctx); return ret; } static void sskdf_variant_digest_ctx_cleanup(sskdf_variant_ctx *ctx) { if (!ctx || !ctx->data) { return; } sskdf_variant_digest_ctx *variant_ctx = (sskdf_variant_digest_ctx *)ctx->data; EVP_MD_CTX_free(variant_ctx->md_ctx); OPENSSL_free(variant_ctx); ctx->data = NULL; } static size_t sskdf_variant_digest_output_size(sskdf_variant_ctx *ctx) { if (!ctx || !ctx->data) { return 0; } sskdf_variant_digest_ctx *variant_ctx = (sskdf_variant_digest_ctx *)ctx->data; return EVP_MD_size(variant_ctx->digest); } static int sskdf_variant_digest_compute( sskdf_variant_ctx *ctx, uint8_t *out, size_t out_len, const uint8_t counter[SSKDF_COUNTER_SIZE], const uint8_t *secret, size_t secret_len, const uint8_t *info, size_t info_len) { if (!ctx || !ctx->data || !out || !counter || !secret) { return 0; } sskdf_variant_digest_ctx *variant_ctx = (sskdf_variant_digest_ctx *)ctx->data; if (!variant_ctx->md_ctx || !variant_ctx->digest) { return 0; } uint32_t written; // NIST.SP.800-56Cr2: Step 6.2 hash(counter || secret || info) if (!EVP_MD_CTX_reset(variant_ctx->md_ctx) || !EVP_DigestInit_ex(variant_ctx->md_ctx, variant_ctx->digest, NULL) || !EVP_DigestUpdate(variant_ctx->md_ctx, &counter[0], SSKDF_COUNTER_SIZE) || !EVP_DigestUpdate(variant_ctx->md_ctx, secret, secret_len) || !EVP_DigestUpdate(variant_ctx->md_ctx, info, info_len) || !EVP_DigestFinal(variant_ctx->md_ctx, out, &written) || written != out_len) { return 0; } return 1; } DEFINE_METHOD_FUNCTION(sskdf_variant, sskdf_variant_digest) { out->h_output_bytes = sskdf_variant_digest_output_size; out->compute = sskdf_variant_digest_compute; } static int sskdf_variant_hmac_ctx_init(sskdf_variant_ctx *ctx, const EVP_MD *digest, const uint8_t *salt, size_t salt_len) { sskdf_variant_hmac_ctx *variant_ctx = NULL; HMAC_CTX *hmac_ctx = NULL; int ret = 0; if (!ctx || ctx->data || !digest) { goto err; } variant_ctx = OPENSSL_malloc(sizeof(sskdf_variant_hmac_ctx)); if (!variant_ctx) { goto err; } hmac_ctx = HMAC_CTX_new(); if (!hmac_ctx) { goto err; } if (!HMAC_Init_ex(hmac_ctx, salt, salt_len, digest, NULL)) { goto err; } ret = 1; variant_ctx->hmac_ctx = hmac_ctx; ctx->data = variant_ctx; return ret; err: HMAC_CTX_free(hmac_ctx); OPENSSL_free(variant_ctx); return ret; } static void sskdf_variant_hmac_ctx_cleanup(sskdf_variant_ctx *ctx) { if (!ctx || !ctx->data) { return; } sskdf_variant_hmac_ctx *variant_ctx = (sskdf_variant_hmac_ctx *)ctx->data; HMAC_CTX_free(variant_ctx->hmac_ctx); OPENSSL_free(variant_ctx); ctx->data = NULL; } static size_t sskdf_variant_hmac_output_size(sskdf_variant_ctx *ctx) { if (!ctx || !ctx->data) { return 0; } sskdf_variant_hmac_ctx *variant_ctx = (sskdf_variant_hmac_ctx *)ctx->data; if (!variant_ctx) { return 0; } return HMAC_size(variant_ctx->hmac_ctx); } static int sskdf_variant_hmac_compute(sskdf_variant_ctx *ctx, uint8_t *out, size_t out_len, const uint8_t counter[SSKDF_COUNTER_SIZE], const uint8_t *secret, size_t secret_len, const uint8_t *info, size_t info_len) { if (!ctx || !ctx->data || !out || !counter || !secret) { return 0; } sskdf_variant_hmac_ctx *variant_ctx = (sskdf_variant_hmac_ctx *)ctx->data; if (!variant_ctx->hmac_ctx) { return 0; } uint32_t written; // NIST.SP.800-56Cr2: Step 6.2 HMAC-hash(salt, counter || secret || info) // Note: |variant_ctx->hmac_ctx| is already initalized with the salt during // its initial construction. if (!HMAC_Init_ex(variant_ctx->hmac_ctx, NULL, 0, NULL, NULL) || !HMAC_Update(variant_ctx->hmac_ctx, &counter[0], SSKDF_COUNTER_SIZE) || !HMAC_Update(variant_ctx->hmac_ctx, secret, secret_len) || !HMAC_Update(variant_ctx->hmac_ctx, info, info_len) || !HMAC_Final(variant_ctx->hmac_ctx, out, &written) || out_len != written) { return 0; } return 1; } DEFINE_METHOD_FUNCTION(sskdf_variant, sskdf_variant_hmac) { out->h_output_bytes = sskdf_variant_hmac_output_size; out->compute = sskdf_variant_hmac_compute; } static int SSKDF(const sskdf_variant *variant, sskdf_variant_ctx *ctx, uint8_t *out_key, size_t out_len, const uint8_t *secret, size_t secret_len, const uint8_t *info, size_t info_len) { int ret = 0; if (!ctx || !variant) { return 0; } // The SSKDF_MAX_INPUT_LEN is an upper bound chosen for improved // interoperability with OpenSSL's SSKDF implementation. Additionally this // upper bound satisfies the NIST.SP.800-56Cr2 requirements outlined in table // 2 and 3 for digest and HMAC variants with approved hashes. The smallest max // limit imposed is (2^64-512)/8 bytes, and the limits used here places the // maximum allowed input to be (2^31 + 4). This satisfies the requirement // outlined in Step 4 of the specification. if (!out_key || out_len == 0 || out_len > SSKDF_MAX_INPUT_LEN || !secret || secret_len == 0 || secret_len > SSKDF_MAX_INPUT_LEN || info_len > SSKDF_MAX_INPUT_LEN) { goto err; } // h_output_bytes is the length in bytes of output of the SSKDF variant // auxilary function (EVP_DigestFinal or HMAC_Final) size_t h_output_bytes = variant->h_output_bytes(ctx); if (h_output_bytes == 0 || h_output_bytes > EVP_MAX_MD_SIZE) { goto err; } // NIST.SP.800-56Cr2 Step 1: // Determine how many output chunks are required to produce the requested // output length |out_len|. This determines how many times the variant compute // function will be called to output key material. uint64_t n = ((uint64_t)out_len + (uint64_t)h_output_bytes - 1) / (uint64_t)h_output_bytes; // NIST.SP.800-56Cr2 Step 2: // Verify that the number of output chunks does not exceed an unsigned 32-bit // integer. if (n > UINT32_MAX) { goto err; } size_t done = 0; // NIST.SP.800-56Cr2 Step 6 for (uint32_t i = 0; i < n; i++) { // TODO(awslc): Abstract buffer size, if we ever need to support KMAC this // could be variable. Currently sufficient for HMAC and digest variants uint8_t out_key_i[EVP_MAX_MD_SIZE]; uint8_t counter[SSKDF_COUNTER_SIZE]; size_t todo; // NIST.SP.800-56Cr2: Step 6.1 // Increment the big-endian counter by one. CRYPTO_store_u32_be(&counter[0], i + 1); // NIST.SP.800-56Cr2: Step 6.2 // Compute out_key_i = H(counter || secret || info) if (!variant->compute(ctx, &out_key_i[0], h_output_bytes, counter, secret, secret_len, info, info_len)) { OPENSSL_cleanse(&out_key_i[0], EVP_MAX_MD_SIZE); goto err; } // NIST.SP.800-56Cr2: Step 6.3. Step 7, Step 8 // Combine the output from |out_key_i| with the output written to |out_key| // so far. Ensure that we only copy |out_len| bytes in total from all // chunks. todo = h_output_bytes; if (todo > out_len - done) { todo = out_len - done; } OPENSSL_memcpy(out_key + done, out_key_i, todo); done += todo; // When we are finished clear the temporary buffer to cleanse key material // from stack. if (done == out_len) { OPENSSL_cleanse(&out_key_i[0], EVP_MAX_MD_SIZE); } } ret = 1; err: if (ret <= 0 && out_key && out_len > 0) { OPENSSL_cleanse(out_key, out_len); } return ret; } int SSKDF_digest(uint8_t *out_key, size_t out_len, const EVP_MD *digest, const uint8_t *secret, size_t secret_len, const uint8_t *info, size_t info_len) { SET_DIT_AUTO_RESET; // We have to avoid the underlying |EVP_DigestFinal| services updating // the indicator state, so we lock the state here. FIPS_service_indicator_lock_state(); sskdf_variant_ctx ctx = {0}; int ret = 0; if (!sskdf_variant_digest_ctx_init(&ctx, digest)) { FIPS_service_indicator_unlock_state(); return 0; } if (!SSKDF(sskdf_variant_digest(), &ctx, out_key, out_len, secret, secret_len, info, info_len)) { goto end; } ret = 1; end: sskdf_variant_digest_ctx_cleanup(&ctx); FIPS_service_indicator_unlock_state(); if (ret) { SSKDF_digest_verify_service_indicator(digest); } return ret; } int SSKDF_hmac(uint8_t *out_key, size_t out_len, const EVP_MD *digest, const uint8_t *secret, size_t secret_len, const uint8_t *info, size_t info_len, const uint8_t *salt, size_t salt_len) { SET_DIT_AUTO_RESET; // We have to avoid the underlying |HMAC_Final| services updating // the indicator state, so we lock the state here. FIPS_service_indicator_lock_state(); sskdf_variant_ctx ctx = {0}; int ret = 0; if (!sskdf_variant_hmac_ctx_init(&ctx, digest, salt, salt_len)) { FIPS_service_indicator_unlock_state(); return 0; } if (!SSKDF(sskdf_variant_hmac(), &ctx, out_key, out_len, secret, secret_len, info, info_len)) { goto end; } ret = 1; end: sskdf_variant_hmac_ctx_cleanup(&ctx); FIPS_service_indicator_unlock_state(); if(ret) { SSKDF_hmac_verify_service_indicator(digest); } return ret; }