Files
cli/vendor/aws-lc-sys/aws-lc/crypto/fipsmodule/hmac/hmac.c

667 lines
25 KiB
C
Raw Normal View History

// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/hmac.h>
#include <assert.h>
#include <string.h>
#include <openssl/digest.h>
#include <openssl/mem.h>
#include "internal.h"
#include "../../internal.h"
#include "../service_indicator/internal.h"
#include "../md5/internal.h"
#include "../sha/internal.h"
typedef int (*HashInit)(void *);
typedef int (*HashUpdate)(void *, const void *, size_t);
typedef int (*HashFinal)(uint8_t *, void *);
typedef int (*HashInitFromState)(void *, const uint8_t *, uint64_t);
typedef int (*HashGetState)(void *, uint8_t *, uint64_t *);
struct hmac_methods_st {
const EVP_MD* evp_md;
size_t chaining_length; // chaining length in bytes
HashInit init;
HashUpdate update;
HashFinal finalize; // Not named final to avoid keywords
HashInitFromState init_from_state;
HashGetState get_state;
};
// We need trampolines from the generic void* methods we use to the properly typed underlying methods.
// Without these methods some control flow integrity checks will fail because the function pointer types
// do not exactly match the destination functions. (Namely function pointers use void* pointers for the contexts)
// while the destination functions have specific pointer types for the relevant contexts.
//
// This also includes hash-specific static assertions as they can be added.
#define MD_TRAMPOLINES_EXPLICIT(HASH_NAME, HASH_CTX, HASH_CBLOCK) \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Init(void *); \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Update(void *, const void *, \
size_t); \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Final(uint8_t *, void *); \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Init(void *ctx) { \
return HASH_NAME##_Init((HASH_CTX *)ctx); \
} \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Update( \
void *ctx, const void *key, size_t key_len) { \
return HASH_NAME##_Update((HASH_CTX *)ctx, key, key_len); \
} \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Final(uint8_t *out, void *ctx) { \
return HASH_NAME##_Final(out, (HASH_CTX *)ctx); \
} \
OPENSSL_STATIC_ASSERT(HASH_CBLOCK % 8 == 0, \
HASH_NAME##_has_blocksize_not_divisible_by_eight_t) \
OPENSSL_STATIC_ASSERT(HASH_CBLOCK <= EVP_MAX_MD_BLOCK_SIZE, \
HASH_NAME##_has_overlarge_blocksize_t) \
OPENSSL_STATIC_ASSERT(sizeof(HASH_CTX) <= sizeof(union md_ctx_union), \
HASH_NAME##_has_overlarge_context_t)
// For merkle-damgard constructions, we also define functions for importing and
// exporting hash state for precomputed keys. These are not applicable to
// Keccak/SHA3.
#define MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(HASH_NAME, HASH_CTX, HASH_CBLOCK) \
MD_TRAMPOLINES_EXPLICIT(HASH_NAME, HASH_CTX, HASH_CBLOCK); \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_Init_from_state( \
void *ctx, const uint8_t *h, uint64_t n) { \
return HASH_NAME##_Init_from_state((HASH_CTX *)ctx, h, n); \
} \
static int AWS_LC_TRAMPOLINE_##HASH_NAME##_get_state( \
void *ctx, uint8_t *out_h, uint64_t *out_n) { \
return HASH_NAME##_get_state((HASH_CTX *)ctx, out_h, out_n); \
} \
OPENSSL_STATIC_ASSERT(HMAC_##HASH_NAME##_PRECOMPUTED_KEY_SIZE == \
2 * HASH_NAME##_CHAINING_LENGTH, \
HASH_NAME##_has_incorrect_precomputed_key_size) \
OPENSSL_STATIC_ASSERT(HMAC_##HASH_NAME##_PRECOMPUTED_KEY_SIZE <= \
HMAC_MAX_PRECOMPUTED_KEY_SIZE, \
HASH_NAME##_has_too_large_precomputed_key_size) \
// The maximum number of HMAC implementations
#define HMAC_METHOD_MAX 12
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(MD5, MD5_CTX, MD5_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA1, SHA_CTX, SHA_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA224, SHA256_CTX, SHA256_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA256, SHA256_CTX, SHA256_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA384, SHA512_CTX, SHA512_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA512, SHA512_CTX, SHA512_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA512_224, SHA512_CTX, SHA512_CBLOCK)
MD_TRAMPOLINES_EXPLICIT_PRECOMPUTED(SHA512_256, SHA512_CTX, SHA512_CBLOCK)
MD_TRAMPOLINES_EXPLICIT(SHA3_224, KECCAK1600_CTX, SHA3_224_CBLOCK)
MD_TRAMPOLINES_EXPLICIT(SHA3_256, KECCAK1600_CTX, SHA3_256_CBLOCK)
MD_TRAMPOLINES_EXPLICIT(SHA3_384, KECCAK1600_CTX, SHA3_384_CBLOCK)
MD_TRAMPOLINES_EXPLICIT(SHA3_512, KECCAK1600_CTX, SHA3_512_CBLOCK)
struct hmac_method_array_st {
HmacMethods methods[HMAC_METHOD_MAX];
};
// This macro does not set any values for precomputed keys for portable state,
// and as such is suitable for use with Keccak/SHA3.
#define DEFINE_IN_PLACE_METHODS(EVP_MD, HASH_NAME) { \
out->methods[idx].evp_md = EVP_MD; \
out->methods[idx].init = AWS_LC_TRAMPOLINE_##HASH_NAME##_Init; \
out->methods[idx].update = AWS_LC_TRAMPOLINE_##HASH_NAME##_Update; \
out->methods[idx].finalize = AWS_LC_TRAMPOLINE_##HASH_NAME##_Final; \
out->methods[idx].chaining_length = 0UL; \
out->methods[idx].init_from_state = NULL; \
out->methods[idx].get_state = NULL; \
idx++; \
assert(idx <= HMAC_METHOD_MAX); \
}
// Use |idx-1| because DEFINE_IN_PLACE_METHODS has already incremented it.
#define DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_MD, HASH_NAME) { \
DEFINE_IN_PLACE_METHODS(EVP_MD, HASH_NAME); \
assert(idx-1 >= 0); \
out->methods[idx-1].chaining_length = HASH_NAME##_CHAINING_LENGTH; \
out->methods[idx-1].init_from_state = \
AWS_LC_TRAMPOLINE_##HASH_NAME##_Init_from_state; \
out->methods[idx-1].get_state = \
AWS_LC_TRAMPOLINE_##HASH_NAME##_get_state; \
}
DEFINE_LOCAL_DATA(struct hmac_method_array_st, AWSLC_hmac_in_place_methods) {
OPENSSL_memset((void*) out->methods, 0, sizeof(out->methods));
int idx = 0;
// Since we search these linearly it helps (just a bit) to put the most common ones first.
// This isn't based on hard metrics and will not make a significant different on performance.
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha256(), SHA256);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha1(), SHA1);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha384(), SHA384);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha512(), SHA512);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_md5(), MD5);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha224(), SHA224);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha512_224(), SHA512_224);
DEFINE_IN_PLACE_METHODS_PRECOMPUTED(EVP_sha512_256(), SHA512_256);
DEFINE_IN_PLACE_METHODS(EVP_sha3_224(), SHA3_224);
DEFINE_IN_PLACE_METHODS(EVP_sha3_256(), SHA3_256);
DEFINE_IN_PLACE_METHODS(EVP_sha3_384(), SHA3_384);
DEFINE_IN_PLACE_METHODS(EVP_sha3_512(), SHA3_512);
}
static const HmacMethods *GetInPlaceMethods(const EVP_MD *evp_md) {
const struct hmac_method_array_st *method_array = AWSLC_hmac_in_place_methods();
const HmacMethods *methods = method_array->methods;
for (size_t idx = 0; idx < sizeof(method_array->methods) / sizeof(struct hmac_methods_st); idx++) {
if (methods[idx].evp_md == evp_md) {
return &methods[idx];
}
}
return NULL;
}
// ctx->state has the following possible states
// (Pre/Post conditions):
// HMAC_STATE_UNINITIALIZED: Uninitialized.
// HMAC_STATE_INIT_NO_DATA: Initialized with an md and key. No data processed.
// This means that if init is called but nothing changes, we don't need to reset our state.
// HMAC_STATE_IN_PROGRESS: Initialized with an md and key. Data processed.
// This means that if init is called we do need to reset state.
// HMAC_STATE_READY_NEEDS_INIT: Identical to state HMAC_STATE_INIT_NO_DATA but API contract requires that Init be called prior to use.
// This is an optimization because we can leave the context in a state ready for use after completion.
// HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY: Identical to state HMAC_STATE_READY_NEEDS_INIT but marked to allow precompute key export
// This state is treated as HMAC_STATE_READY_NEEDS_INIT by Init/Update/Final.
// This state is the only state that in which a precompute key can be exported.
// This state is set by HMAC_set_precomputed_key_export.
// other: Invalid state and likely a result of using unitialized memory. Treated the same as 0.
//
// While we are within HMAC methods we allow for the state value and actual state of the context to diverge.
// HMAC_STATE_UNINITIALIZED *MUST* remain `0` so that callers can do `HMAC_CTX ctx = {0};` to get a usable context.
#define HMAC_STATE_UNINITIALIZED 0
#define HMAC_STATE_INIT_NO_DATA 1
#define HMAC_STATE_IN_PROGRESS 2
#define HMAC_STATE_READY_NEEDS_INIT 3
#define HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY 4
// Static assertion to ensure that no one has changed the value of HMAC_STATE_UNINITIALIZED.
// This really must stay with a zero value.
OPENSSL_STATIC_ASSERT(HMAC_STATE_UNINITIALIZED == 0, HMAC_STATE_UNINITIALIZED_is_not_zero_t)
// Indicates that a context has the md and methods configured and is ready to use
#define hmac_ctx_is_initialized(ctx) ((HMAC_STATE_INIT_NO_DATA == (ctx)->state || HMAC_STATE_IN_PROGRESS == (ctx)->state))
uint8_t *HMAC(const EVP_MD *evp_md, const void *key, size_t key_len,
const uint8_t *data, size_t data_len, uint8_t *out,
unsigned int *out_len) {
if (out == NULL) {
// Prevent further work from being done
return NULL;
}
HMAC_CTX ctx;
OPENSSL_memset(&ctx, 0, sizeof(HMAC_CTX));
int result;
// We have to avoid the underlying SHA services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
result = HMAC_Init_ex(&ctx, key, key_len, evp_md, NULL) &&
HMAC_Update(&ctx, data, data_len) &&
HMAC_Final(&ctx, out, out_len);
FIPS_service_indicator_unlock_state();
// Regardless of our success we need to zeroize our working state.
HMAC_CTX_cleanup(&ctx);
if (result) {
HMAC_verify_service_indicator(evp_md);
return out;
} else {
OPENSSL_cleanse(out, EVP_MD_size(evp_md));
return NULL;
}
}
uint8_t *HMAC_with_precompute(const EVP_MD *evp_md, const void *key,
size_t key_len, const uint8_t *data,
size_t data_len, uint8_t *out,
unsigned int *out_len) {
if (out == NULL) {
// Prevent further work from being done
return NULL;
}
HMAC_CTX ctx;
OPENSSL_memset(&ctx, 0, sizeof(HMAC_CTX));
int result;
// We have to avoid the underlying SHA services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
uint8_t precomputed_key[HMAC_MAX_PRECOMPUTED_KEY_SIZE];
size_t precomputed_key_len = HMAC_MAX_PRECOMPUTED_KEY_SIZE;
result =
HMAC_Init_ex(&ctx, key, key_len, evp_md, NULL) &&
HMAC_set_precomputed_key_export(&ctx) &&
HMAC_get_precomputed_key(&ctx, precomputed_key, &precomputed_key_len) &&
HMAC_Init_from_precomputed_key(&ctx, precomputed_key, precomputed_key_len,
evp_md) &&
HMAC_Update(&ctx, data, data_len) &&
HMAC_Final(&ctx, out, out_len);
FIPS_service_indicator_unlock_state();
// Regardless of our success we need to zeroize our working state.
HMAC_CTX_cleanup(&ctx);
OPENSSL_cleanse(precomputed_key, HMAC_MAX_PRECOMPUTED_KEY_SIZE);
if (result) {
// Contrary to what happens in the |HMAC| function, we do not update the
// service indicator here (i.e., we do not call
// |HMAC_verify_service_indicator|), because the function
// |HMAC_with_precompute| is not FIPS-approved per se and is only used in
// tests.
return out;
} else {
OPENSSL_cleanse(out, EVP_MD_size(evp_md));
return NULL;
}
}
void HMAC_CTX_init(HMAC_CTX *ctx) {
OPENSSL_memset(ctx, 0, sizeof(HMAC_CTX));
}
HMAC_CTX *HMAC_CTX_new(void) {
HMAC_CTX *ctx = OPENSSL_zalloc(sizeof(HMAC_CTX));
if (ctx != NULL) {
// NO-OP: struct already zeroed
//HMAC_CTX_init(ctx);
}
return ctx;
}
void HMAC_CTX_cleanup(HMAC_CTX *ctx) {
// All of the contexts are flat and can simply be zeroed
OPENSSL_cleanse(ctx, sizeof(HMAC_CTX));
}
void HMAC_CTX_cleanse(HMAC_CTX *ctx) {
HMAC_CTX_cleanup(ctx);
}
void HMAC_CTX_free(HMAC_CTX *ctx) {
if (ctx == NULL) {
return;
}
HMAC_CTX_cleanup(ctx);
OPENSSL_free(ctx);
}
// hmac_ctx_set_md_methods is used to set ctx->methods and ctx->md from md.
// It is called as part of the initialization of the ctx (HMAC_Init_*).
// It returns one on success, and zero otherwise.
static int hmac_ctx_set_md_methods(HMAC_CTX *ctx, const EVP_MD *md) {
if (md && (HMAC_STATE_UNINITIALIZED == ctx->state || ctx->md != md)) {
// The MD has changed
ctx->methods = GetInPlaceMethods(md);
if (ctx->methods == NULL) {
// Unsupported md
return 0;
}
ctx->md = md;
} else if (!hmac_ctx_is_initialized(ctx)) {
// We are not initialized but have not been provided with an md to
// initialize ourselves with.
return 0;
}
return 1;
}
int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
const EVP_MD *md, ENGINE *impl) {
assert(impl == NULL);
GUARD_PTR(ctx);
// HMAC does not support SHAKE (XOF) algorithms
if (md && (EVP_MD_flags(md) & EVP_MD_FLAG_XOF)) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_UNSUPPORTED_DIGEST);
return 0;
}
if (HMAC_STATE_READY_NEEDS_INIT == ctx->state ||
HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY == ctx->state) {
ctx->state = HMAC_STATE_INIT_NO_DATA; // Mark that init has been called
}
if (hmac_ctx_is_initialized(ctx)) {
// TODO(davidben,eroman): Passing the previous |md| with a NULL |key| is
// ambiguous between using the empty key and reusing the previous key. There
// exist callers which intend the latter, but the former is an awkward edge
// case. Fix to API to avoid this.
if (key == NULL && (md == NULL || md == ctx->md)) {
if(HMAC_STATE_IN_PROGRESS == ctx->state) {
// Reinitialize |md_ctx| from |i_ctx| to start fresh with the same key. This
// is the same behavior as Openssl.
OPENSSL_memcpy(&ctx->md_ctx, &ctx->i_ctx, sizeof(ctx->i_ctx));
}
// If nothing is changing then we can return without doing any further work.
ctx->state = HMAC_STATE_INIT_NO_DATA;
return 1;
}
}
// At this point we *know* we need to change things and rekey because either the key has changed
// or the md and they key has changed.
// (It is a misuse to just change the md so we also assume that the key changes when the md changes.)
if (!hmac_ctx_set_md_methods(ctx, md)) {
return 0;
}
// At this point we know we have valid methods and a context allocated.
const HmacMethods *methods = ctx->methods;
size_t block_size = EVP_MD_block_size(methods->evp_md);
assert(block_size % 8 == 0);
assert(block_size <= EVP_MAX_MD_BLOCK_SIZE);
// We have to avoid the underlying SHA services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
int result = 0;
uint64_t pad[EVP_MAX_MD_BLOCK_SIZE / sizeof(uint64_t)] = {0};
uint64_t key_block[EVP_MAX_MD_BLOCK_SIZE / sizeof(uint64_t)] = {0};
if (block_size < key_len) {
// Long keys are hashed.
if (!methods->init(&ctx->md_ctx) ||
!methods->update(&ctx->md_ctx, key, key_len) ||
!methods->finalize((uint8_t *) key_block, &ctx->md_ctx)) {
goto end;
}
} else {
assert(key_len <= sizeof(key_block));
OPENSSL_memcpy(key_block, key, key_len);
}
for (size_t i = 0; i < block_size / 8; i++) {
pad[i] = 0x3636363636363636 ^ key_block[i];
}
if (!methods->init(&ctx->i_ctx) ||
!methods->update(&ctx->i_ctx, pad, block_size)) {
goto end;
}
for (size_t i = 0; i < block_size / 8; i++) {
pad[i] = 0x5c5c5c5c5c5c5c5c ^ key_block[i];
}
if (!methods->init(&ctx->o_ctx) ||
!methods->update(&ctx->o_ctx, pad, block_size)) {
goto end;
}
OPENSSL_memcpy(&ctx->md_ctx, &ctx->i_ctx, sizeof(ctx->i_ctx));
ctx->state = HMAC_STATE_INIT_NO_DATA;
result = 1;
end:
OPENSSL_cleanse(pad, EVP_MAX_MD_BLOCK_SIZE);
OPENSSL_cleanse(key_block, EVP_MAX_MD_BLOCK_SIZE);
FIPS_service_indicator_unlock_state();
if (result != 1) {
// We're in some error state, so return our context to a known and well defined zero state.
HMAC_CTX_cleanup(ctx);
}
return result;
}
int HMAC_Update(HMAC_CTX *ctx, const uint8_t *data, size_t data_len) {
if (!hmac_ctx_is_initialized(ctx)) {
return 0;
}
ctx->state = HMAC_STATE_IN_PROGRESS;
return ctx->methods->update(&ctx->md_ctx, data, data_len);
}
int HMAC_Final(HMAC_CTX *ctx, uint8_t *out, unsigned int *out_len) {
if (out == NULL) {
return 0;
}
const HmacMethods *methods = ctx->methods;
if (!hmac_ctx_is_initialized(ctx)) {
return 0;
}
// We have to avoid the underlying SHA services updating the indicator
// state, so we lock the state here.
FIPS_service_indicator_lock_state();
int result = 0;
const EVP_MD *evp_md = ctx->md;
int hmac_len = EVP_MD_size(evp_md);
uint8_t tmp[EVP_MAX_MD_SIZE];
if (!methods->finalize(tmp, &ctx->md_ctx)) {
goto end;
}
OPENSSL_memcpy(&ctx->md_ctx, &ctx->o_ctx, sizeof(ctx->o_ctx));
if (!ctx->methods->update(&ctx->md_ctx, tmp, hmac_len)) {
goto end;
}
result = methods->finalize(out, &ctx->md_ctx);
// Wipe out working state by initializing for next use
OPENSSL_memcpy(&ctx->md_ctx, &ctx->i_ctx, sizeof(ctx->i_ctx));
ctx->state = HMAC_STATE_READY_NEEDS_INIT; // Mark that we are ready for use but still need HMAC_Init_ex called.
end:
// Cleanse sensitive intermediate inner hash from the stack.
OPENSSL_cleanse(tmp, sizeof(tmp));
FIPS_service_indicator_unlock_state();
if (result) {
HMAC_verify_service_indicator(evp_md);
if (out_len) {
*out_len = hmac_len;
}
return 1;
} else {
// On error, return context to a known and well-defined zero state.
HMAC_CTX_cleanup(ctx);
if (out_len) {
*out_len = 0;
}
return 0;
}
}
size_t HMAC_size(const HMAC_CTX *ctx) { return EVP_MD_size(ctx->md); }
const EVP_MD *HMAC_CTX_get_md(const HMAC_CTX *ctx) { return ctx->md; }
int HMAC_CTX_copy_ex(HMAC_CTX *dest, const HMAC_CTX *src) {
OPENSSL_memcpy(dest, src, sizeof(HMAC_CTX));
return 1;
}
void HMAC_CTX_reset(HMAC_CTX *ctx) {
HMAC_CTX_cleanup(ctx);
// Cleanup intrinsicly inits to all zeros which is valid
}
int HMAC_set_precomputed_key_export(HMAC_CTX *ctx) {
GUARD_PTR(ctx);
if (ctx->methods != NULL && ctx->methods->get_state == NULL) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_PRECOMPUTED_KEY_NOT_SUPPORTED_FOR_DIGEST);
return 0;
}
if (HMAC_STATE_INIT_NO_DATA != ctx->state &&
HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY != ctx->state) {
// HMAC_set_precomputed_key_export can only be called after Hmac_Init_*
OPENSSL_PUT_ERROR(HMAC, HMAC_R_NOT_CALLED_JUST_AFTER_INIT);
return 0;
}
ctx->state = HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY;
return 1;
}
int HMAC_get_precomputed_key(HMAC_CTX *ctx, uint8_t *out, size_t *out_len) {
GUARD_PTR(ctx);
GUARD_PTR(ctx->methods);
if (ctx->methods->get_state == NULL) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_PRECOMPUTED_KEY_NOT_SUPPORTED_FOR_DIGEST);
return 0;
}
if (HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY != ctx->state) {
OPENSSL_PUT_ERROR(EVP, HMAC_R_SET_PRECOMPUTED_KEY_EXPORT_NOT_CALLED);
return 0;
}
if (NULL == out_len) {
OPENSSL_PUT_ERROR(EVP, HMAC_R_MISSING_PARAMETERS);
return 0;
}
const size_t chaining_length = ctx->methods->chaining_length;
size_t actual_out_len = chaining_length * 2;
assert(actual_out_len <= HMAC_MAX_PRECOMPUTED_KEY_SIZE);
if (NULL == out) {
// When out is NULL, we just set out_len.
// We keep the state as HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY
// to allow an actual export of the precomputed key immediately afterward.
*out_len = actual_out_len;
return 1;
}
// When out is not NULL, we need to check that *out_len is large enough
if (*out_len < actual_out_len) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_BUFFER_TOO_SMALL);
return 0;
}
*out_len = actual_out_len;
uint64_t i_ctx_n;
// Initializing o_ctx_n to zero to remove warning from Windows ARM64 compiler
// "error : variable 'o_ctx_n' is used uninitialized whenever '&&' condition
// is false". Note this should not be necessary because get_state cannot fail.
uint64_t o_ctx_n = 0;
if (!ctx->methods->get_state(&ctx->i_ctx, out, &i_ctx_n) ||
!ctx->methods->get_state(&ctx->o_ctx, out + chaining_length, &o_ctx_n)) {
// get_state should always succeed since i_ctx/o_ctx have processed exactly
// one block, but handle failure defensively.
assert(0); // Should never happen
OPENSSL_cleanse(out, actual_out_len);
return 0;
}
// Sanity check: we must have processed a single block at this time
size_t block_size = EVP_MD_block_size(ctx->md);
if (8 * block_size != i_ctx_n || 8 * block_size != o_ctx_n) {
assert(0); // Should never happen
OPENSSL_cleanse(out, actual_out_len);
return 0;
}
// The context is ready to be used to compute HMAC values at this point.
ctx->state = HMAC_STATE_INIT_NO_DATA;
return 1;
}
int HMAC_Init_from_precomputed_key(HMAC_CTX *ctx,
const uint8_t *precomputed_key,
size_t precomputed_key_len,
const EVP_MD *md) {
// HMAC does not support SHAKE (XOF) algorithms
if (md && (EVP_MD_flags(md) & EVP_MD_FLAG_XOF)) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_UNSUPPORTED_DIGEST);
return 0;
}
if (HMAC_STATE_READY_NEEDS_INIT == ctx->state ||
HMAC_STATE_PRECOMPUTED_KEY_EXPORT_READY == ctx->state) {
ctx->state = HMAC_STATE_INIT_NO_DATA; // Mark that init has been called
}
if (HMAC_STATE_INIT_NO_DATA == ctx->state) {
if (precomputed_key == NULL && (md == NULL || md == ctx->md)) {
// If nothing is changing then we can return without doing any further
// work.
return 1;
}
}
// Now we assume that we need to re-initialize everything.
// See HMAC_Init_ex
if (!hmac_ctx_set_md_methods(ctx, md)) {
return 0;
}
const HmacMethods *methods = ctx->methods;
if (ctx->methods->init_from_state == NULL) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_PRECOMPUTED_KEY_NOT_SUPPORTED_FOR_DIGEST);
return 0;
}
const size_t chaining_length = methods->chaining_length;
const size_t block_size = EVP_MD_block_size(methods->evp_md);
assert(block_size <= EVP_MAX_MD_BLOCK_SIZE);
assert(2 * chaining_length <= HMAC_MAX_PRECOMPUTED_KEY_SIZE);
if (2 * chaining_length != precomputed_key_len) {
return 0;
}
// We require precomputed_key to be non-NULL, since here md changed
if (NULL == precomputed_key) {
OPENSSL_PUT_ERROR(HMAC, HMAC_R_MISSING_PARAMETERS);
return 0;
}
// We have to avoid the underlying SHA services updating the indicator
// state, so we lock the state here. Technically this is not really needed,
// because the functions we call should not update the indicator state.
// But this is safer.
FIPS_service_indicator_lock_state();
int result = 0;
// Initialize i_ctx from the state stored in the first part of precomputed_key
// Recall that i_ctx is the state of the hash function after processing
// one block (ipad xor keyOrHashedKey)
if (!methods->init_from_state(&ctx->i_ctx, precomputed_key, block_size * 8)) {
goto end;
}
// Same for o_ctx using the second part of precomputed_key
if (!methods->init_from_state(&ctx->o_ctx, precomputed_key + chaining_length,
block_size * 8)) {
goto end;
}
OPENSSL_memcpy(&ctx->md_ctx, &ctx->i_ctx, sizeof(ctx->i_ctx));
ctx->state = HMAC_STATE_INIT_NO_DATA;
result = 1;
end:
FIPS_service_indicator_unlock_state();
if (result != 1) {
// We're in some error state, so return our context to a known and
// well-defined zero state.
HMAC_CTX_cleanup(ctx);
}
return result;
}
int HMAC_Init(HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md) {
if (key && key_len < 0) {
return 0;
}
if (key && md) {
HMAC_CTX_init(ctx);
}
return HMAC_Init_ex(ctx, key, (size_t)key_len, md, NULL);
}
int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src) {
HMAC_CTX_init(dest);
return HMAC_CTX_copy_ex(dest, src);
}