Files
wolfssl/wolfcrypt/src/wc_she.c
T

1114 lines
38 KiB
C

/* wc_she.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/*
* SHE (Secure Hardware Extension) key update message generation.
*
* Software-only computation of M1/M2/M3 for CMD_LOAD_KEY and optional
* M4/M5 verification. Ported from the wolfHSM reference implementation
* (src/wh_she_crypto.c) and adapted to wolfSSL conventions.
*/
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#ifdef WOLFSSL_SHE
#ifdef NO_AES
#error "SHE requires AES (NO_AES is defined)"
#endif
#ifndef HAVE_AES_CBC
#error "SHE requires AES-CBC (HAVE_AES_CBC is not defined)"
#endif
#ifndef WOLFSSL_AES_DIRECT
#error "SHE requires AES direct (WOLFSSL_AES_DIRECT is not defined)"
#endif
#ifndef WOLFSSL_CMAC
#error "SHE requires CMAC (WOLFSSL_CMAC is not defined)"
#endif
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/cmac.h>
#include <wolfssl/wolfcrypt/wc_she.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#ifdef WOLF_CRYPTO_CB
#include <wolfssl/wolfcrypt/cryptocb.h>
#endif
#ifdef WC_SHE_SW_DEFAULT
/* Software-only default UID for example usage only. Uses the SHE specification
* test vector UID value. Override by defining WC_SHE_DEFAULT_UID before
* including this file. */
#ifndef WC_SHE_DEFAULT_UID
#define WC_SHE_DEFAULT_UID { \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 \
}
#endif
static const byte wc_She_DefaultUid[] = WC_SHE_DEFAULT_UID;
/* Software-only default counter start value for testing. Uses the SHE
* specification test vector counter value. Override by defining
* WC_SHE_DEFAULT_COUNTER before including this file. */
#ifndef WC_SHE_DEFAULT_COUNTER
#define WC_SHE_DEFAULT_COUNTER 1
#endif
#endif /* WC_SHE_SW_DEFAULT */
/* -------------------------------------------------------------------------- */
/* Miyaguchi-Preneel AES-128 compression (internal) */
/* */
/* H_0 = 0 */
/* H_i = E_{H_{i-1}}(M_i) XOR M_i XOR H_{i-1} */
/* */
/* Only valid for AES-128 where key size == block size. */
/* */
/* Ported from wolfHSM wh_She_AesMp16_ex() in src/wh_she_crypto.c. */
/* The caller (GenerateM1M2M3 / GenerateM4M5) owns the Aes object. */
/* -------------------------------------------------------------------------- */
int wc_SHE_AesMp16(Aes* aes, const byte* in, word32 inSz, byte* out)
{
int ret;
int i = 0;
int j;
byte paddedInput[AES_BLOCK_SIZE];
byte prev[WC_SHE_KEY_SZ] = {0};
if (aes == NULL || in == NULL || inSz == 0 || out == NULL) {
return BAD_FUNC_ARG;
}
/* Set initial key = H_0 = all zeros */
ret = wc_AesSetKeyDirect(aes, prev, AES_BLOCK_SIZE, NULL,
AES_ENCRYPTION);
while (ret == 0 && i < (int)inSz) {
/* Copy next input block, zero-padding if short */
if ((int)inSz - i < (int)AES_BLOCK_SIZE) {
XMEMCPY(paddedInput, in + i, inSz - i);
XMEMSET(paddedInput + (inSz - i), 0,
AES_BLOCK_SIZE - (inSz - i));
}
else {
XMEMCPY(paddedInput, in + i, AES_BLOCK_SIZE);
}
/* E_{H_{i-1}}(M_i) */
ret = wc_AesEncryptDirect(aes, out, paddedInput);
if (ret == 0) {
/* H_i = E_{H_{i-1}}(M_i) XOR M_i XOR H_{i-1} */
for (j = 0; j < (int)AES_BLOCK_SIZE; j++) {
out[j] ^= paddedInput[j];
out[j] ^= prev[j];
}
/* Save H_i as the previous output */
XMEMCPY(prev, out, AES_BLOCK_SIZE);
/* Set key = H_i for next block */
ret = wc_AesSetKeyDirect(aes, out, AES_BLOCK_SIZE,
NULL, AES_ENCRYPTION);
i += AES_BLOCK_SIZE;
}
}
return ret;
}
/* -------------------------------------------------------------------------- */
/* Context init */
/* */
/* Zero-initialize the SHE context and store the heap hint and device ID */
/* for use by subsequent crypto operations. */
/* -------------------------------------------------------------------------- */
int wc_SHE_Init(wc_SHE* she, void* heap, int devId)
{
if (she == NULL) {
return BAD_FUNC_ARG;
}
ForceZero(she, sizeof(wc_SHE));
she->heap = heap;
she->devId = devId;
return 0;
}
#ifdef WOLF_PRIVATE_KEY_ID
/* -------------------------------------------------------------------------- */
/* Context init with opaque hardware key identifier */
/* */
/* Like wc_SHE_Init but also stores an opaque byte-string key ID that */
/* crypto callback backends can use to look up the authorizing key in */
/* hardware (e.g. an HSM slot reference or PKCS#11 object handle). */
/* -------------------------------------------------------------------------- */
int wc_SHE_Init_Id(wc_SHE* she, unsigned char* id, int len,
void* heap, int devId)
{
int ret;
if (she == NULL || id == NULL) {
return BAD_FUNC_ARG;
}
if (len < 0 || len > WC_SHE_MAX_ID_LEN) {
return BUFFER_E;
}
ret = wc_SHE_Init(she, heap, devId);
if (ret != 0) {
return ret;
}
XMEMCPY(she->id, id, (size_t)len);
she->idLen = len;
she->labelLen = 0;
return 0;
}
/* -------------------------------------------------------------------------- */
/* Context init with human-readable key label */
/* */
/* Like wc_SHE_Init but also stores a NUL-terminated string label that */
/* crypto callback backends can use for string-based key lookup. */
/* -------------------------------------------------------------------------- */
int wc_SHE_Init_Label(wc_SHE* she, const char* label,
void* heap, int devId)
{
int ret;
size_t labelLen;
if (she == NULL || label == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_SHE_Init(she, heap, devId);
if (ret != 0) {
return ret;
}
labelLen = XSTRLEN(label);
if (labelLen == 0 || labelLen > WC_SHE_MAX_LABEL_LEN) {
return BUFFER_E;
}
XMEMCPY(she->label, label, labelLen);
she->labelLen = (int)labelLen;
she->idLen = 0;
return 0;
}
#endif /* WOLF_PRIVATE_KEY_ID */
/* -------------------------------------------------------------------------- */
/* Context free */
/* */
/* Scrub all key material and reset the SHE context to zero. */
/* Safe to call on a NULL or already-freed context. */
/* -------------------------------------------------------------------------- */
void wc_SHE_Free(wc_SHE* she)
{
if (she == NULL) {
return;
}
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE)
if (she->devId != INVALID_DEVID) {
int ret = wc_CryptoCb_Free(she->devId, WC_ALGO_TYPE_SHE,
0, 0, she);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return;
}
/* fall-through when unavailable */
}
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_FREE */
ForceZero(she, sizeof(wc_SHE));
}
/* -------------------------------------------------------------------------- */
/* GetUID */
/* */
/* When a crypto callback is registered, it can be used to get the UID from */
/* hardware. The caller can pass a challenge or other context via the void */
/* ctx parameter (e.g. challenge buffer, HSM handle). */
/* Returns CRYPTOCB_UNAVAILABLE if no callback. */
/* -------------------------------------------------------------------------- */
#if defined(WOLF_CRYPTO_CB) && !defined(NO_WC_SHE_GETUID)
int wc_SHE_GetUID(wc_SHE* she, byte* uid, word32 uidSz,
const void* ctx)
{
int ret;
if (she == NULL || uid == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_CryptoCb_SheGetUid(she, uid, uidSz, ctx);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return ret;
}
#ifdef WC_SHE_SW_DEFAULT
/* Software-only default UID for example usage only. */
if (uidSz < sizeof(wc_She_DefaultUid)) {
return BUFFER_E;
}
XMEMCPY(uid, wc_She_DefaultUid, sizeof(wc_She_DefaultUid));
ret = 0;
#endif
return ret;
}
#endif /* WOLF_CRYPTO_CB && !NO_WC_SHE_GETUID */
/* -------------------------------------------------------------------------- */
/* GetCounter */
/* */
/* When a crypto callback is registered, it can be used to read the */
/* monotonic counter from hardware. The caller can pass operational context */
/* via the void ctx parameter (e.g. read counter/increment, read only). */
/* Returns CRYPTOCB_UNAVAILABLE if no callback. */
/* -------------------------------------------------------------------------- */
#if defined(WOLF_CRYPTO_CB) && !defined(NO_WC_SHE_GETCOUNTER)
int wc_SHE_GetCounter(wc_SHE* she, word32* counter, const void* ctx)
{
int ret;
#ifdef WC_SHE_SW_DEFAULT
/* Software-only default counter for example usage only.
* Simple static counter that increments on each call. */
static word32 she_sw_counter = WC_SHE_DEFAULT_COUNTER;
#endif
if (she == NULL || counter == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_CryptoCb_SheGetCounter(she, counter, ctx);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return ret;
}
#ifdef WC_SHE_SW_DEFAULT
*counter = she_sw_counter++;
ret = 0;
#endif
return ret;
}
#endif /* WOLF_CRYPTO_CB && !NO_WC_SHE_GETCOUNTER */
/* -------------------------------------------------------------------------- */
/* Extended SHE overrides */
/* -------------------------------------------------------------------------- */
#ifdef WOLFSSL_SHE_EXTENDED
int wc_SHE_SetKdfConstants(wc_SHE* she,
const byte* encC, word32 encCSz,
const byte* macC, word32 macCSz)
{
if (she == NULL) {
return BAD_FUNC_ARG;
}
if (encC != NULL) {
if (encCSz != WC_SHE_KEY_SZ) {
return BAD_FUNC_ARG;
}
XMEMCPY(she->kdfEncC, encC, WC_SHE_KEY_SZ);
she->kdfEncOverride = 1;
}
if (macC != NULL) {
if (macCSz != WC_SHE_KEY_SZ) {
return BAD_FUNC_ARG;
}
XMEMCPY(she->kdfMacC, macC, WC_SHE_KEY_SZ);
she->kdfMacOverride = 1;
}
return 0;
}
#endif /* WOLFSSL_SHE_EXTENDED */
#if defined(WOLF_CRYPTO_CB) && !defined(NO_WC_SHE_IMPORT_M123)
/* -------------------------------------------------------------------------- */
/* Import M1/M2/M3 */
/* */
/* Copy externally-provided M1/M2/M3 into context and set generated flag. */
/* -------------------------------------------------------------------------- */
int wc_SHE_ImportM1M2M3(wc_SHE* she,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz)
{
if (she == NULL || m1 == NULL || m2 == NULL || m3 == NULL) {
return BAD_FUNC_ARG;
}
if (m1Sz != WC_SHE_M1_SZ || m2Sz != WC_SHE_M2_SZ ||
m3Sz != WC_SHE_M3_SZ) {
return BAD_FUNC_ARG;
}
XMEMCPY(she->m1, m1, WC_SHE_M1_SZ);
XMEMCPY(she->m2, m2, WC_SHE_M2_SZ);
XMEMCPY(she->m3, m3, WC_SHE_M3_SZ);
she->generated = 1;
return 0;
}
#endif /* WOLF_CRYPTO_CB && !NO_WC_SHE_IMPORT_M123 */
/* -------------------------------------------------------------------------- */
/* Portable big-endian 32-bit store */
/* -------------------------------------------------------------------------- */
static WC_INLINE void she_store_be32(byte* dst, word32 val)
{
dst[0] = (byte)(val >> 24);
dst[1] = (byte)(val >> 16);
dst[2] = (byte)(val >> 8);
dst[3] = (byte)(val);
}
/* Build M2P and M4P headers from counter and flags using standard SHE packing.
* M2P header: counter(28b) | flags(4b) | zeros(96b) = 16 bytes
* M4P header: counter(28b) | 1(1b) | zeros(99b) = 16 bytes
* Writes to caller-provided buffers. Skipped if WOLFSSL_SHE_EXTENDED
* override is active on the context. */
static void she_build_headers(wc_SHE* she, word32 counter, byte flags,
byte* m2pHeader, byte* m4pHeader)
{
word32 field;
#ifdef WOLFSSL_SHE_EXTENDED
if (she->m2pOverride) {
XMEMCPY(m2pHeader, she->m2pHeader, WC_SHE_KEY_SZ);
}
else
#endif
{
XMEMSET(m2pHeader, 0, WC_SHE_KEY_SZ);
field = (counter << WC_SHE_M2_COUNT_SHIFT) |
(flags << WC_SHE_M2_FLAGS_SHIFT);
she_store_be32(m2pHeader, field);
}
#ifdef WOLFSSL_SHE_EXTENDED
if (she->m4pOverride) {
XMEMCPY(m4pHeader, she->m4pHeader, WC_SHE_KEY_SZ);
}
else
#endif
{
XMEMSET(m4pHeader, 0, WC_SHE_KEY_SZ);
field = (counter << WC_SHE_M4_COUNT_SHIFT) | WC_SHE_M4_COUNT_PAD;
she_store_be32(m4pHeader, field);
}
(void)she;
}
#ifdef WOLFSSL_SHE_EXTENDED
int wc_SHE_SetM2Header(wc_SHE* she, const byte* header, word32 headerSz)
{
if (she == NULL || header == NULL || headerSz != WC_SHE_KEY_SZ) {
return BAD_FUNC_ARG;
}
XMEMCPY(she->m2pHeader, header, WC_SHE_KEY_SZ);
she->m2pOverride = 1;
return 0;
}
int wc_SHE_SetM4Header(wc_SHE* she, const byte* header, word32 headerSz)
{
if (she == NULL || header == NULL || headerSz != WC_SHE_KEY_SZ) {
return BAD_FUNC_ARG;
}
XMEMCPY(she->m4pHeader, header, WC_SHE_KEY_SZ);
she->m4pOverride = 1;
return 0;
}
#endif /* WOLFSSL_SHE_EXTENDED */
/* -------------------------------------------------------------------------- */
/* M1/M2/M3 generation */
/* */
/* Derives K1 and K2 from AuthKey via Miyaguchi-Preneel, then builds: */
/* M1 = UID | TargetKeyID | AuthKeyID */
/* M2 = AES-CBC(K1, IV=0, counter|flags|pad|newkey) */
/* M3 = AES-CMAC(K2, M1 | M2) */
/* */
/* When a crypto callback is registered and the SHE context has a valid */
/* device ID, the callback is tried first. This is useful when a secure */
/* element or HSM holds the auth key internally and can generate M1/M2/M3 */
/* directly. If the callback returns CRYPTOCB_UNAVAILABLE, the software */
/* path runs. */
/* */
/* Ported from wolfHSM wh_She_GenerateLoadableKey() in wh_she_crypto.c. */
/* -------------------------------------------------------------------------- */
int wc_SHE_GenerateM1M2M3(wc_SHE* she,
const byte* uid, word32 uidSz,
byte authKeyId, const byte* authKey, word32 authKeySz,
byte targetKeyId, const byte* newKey, word32 newKeySz,
word32 counter, byte flags,
byte* m1, word32 m1Sz,
byte* m2, word32 m2Sz,
byte* m3, word32 m3Sz)
{
int ret = 0;
byte m2pHeader[WC_SHE_KEY_SZ];
byte m4pHeader[WC_SHE_KEY_SZ];
byte k1[WC_SHE_KEY_SZ];
byte k2[WC_SHE_KEY_SZ];
byte kdfInput[WC_SHE_KEY_SZ * 2];
byte encC[] = WC_SHE_KEY_UPDATE_ENC_C;
byte macC[] = WC_SHE_KEY_UPDATE_MAC_C;
word32 cmacSz = AES_BLOCK_SIZE;
WC_DECLARE_VAR(aes, Aes, 1, 0);
WC_DECLARE_VAR(cmac, Cmac, 1, 0);
/* Validate SHE context first -- required for both callback and software */
if (she == NULL) {
return BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
/* Try callback first -- callback handles its own parameter validation.
* This allows callers to pass NULL authKey/newKey when a secure element
* holds the keys and the callback talks to it directly. */
if (she->devId != INVALID_DEVID) {
ret = wc_CryptoCb_SheGenerateM1M2M3(she, uid, uidSz,
authKeyId, authKey, authKeySz,
targetKeyId, newKey, newKeySz,
counter, flags,
m1, m1Sz, m2, m2Sz, m3, m3Sz);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return ret;
}
/* fall-through to software path */
ret = 0;
}
#endif
/* Software path -- validate all parameters */
if (uid == NULL || uidSz != WC_SHE_UID_SZ ||
authKey == NULL || authKeySz != WC_SHE_KEY_SZ ||
newKey == NULL || newKeySz != WC_SHE_KEY_SZ ||
m1 == NULL || m1Sz < WC_SHE_M1_SZ ||
m2 == NULL || m2Sz < WC_SHE_M2_SZ ||
m3 == NULL || m3Sz < WC_SHE_M3_SZ) {
return BAD_FUNC_ARG;
}
/* Override KDF constants if explicitly set */
#ifdef WOLFSSL_SHE_EXTENDED
if (she->kdfEncOverride) {
XMEMCPY(encC, she->kdfEncC, WC_SHE_KEY_SZ);
}
if (she->kdfMacOverride) {
XMEMCPY(macC, she->kdfMacC, WC_SHE_KEY_SZ);
}
#endif
/* Build M2P/M4P headers from counter/flags (skipped if overridden) */
she_build_headers(she, counter, flags, m2pHeader, m4pHeader);
WC_ALLOC_VAR(aes, Aes, 1, she->heap);
if (!WC_VAR_OK(aes)) {
return MEMORY_E;
}
WC_ALLOC_VAR(cmac, Cmac, 1, she->heap);
if (!WC_VAR_OK(cmac)) {
WC_FREE_VAR(aes, she->heap);
return MEMORY_E;
}
/* Init AES once -- used by both MP16 and CBC */
ret = wc_AesInit(aes, she->heap, she->devId);
if (ret != 0) {
WC_FREE_VAR(aes, she->heap);
WC_FREE_VAR(cmac, she->heap);
return ret;
}
/* ---- Derive K1 = AES-MP(AuthKey || CENC) ---- */
XMEMCPY(kdfInput, authKey, WC_SHE_KEY_SZ);
XMEMCPY(kdfInput + WC_SHE_KEY_SZ, encC, WC_SHE_KEY_SZ);
ret = wc_SHE_AesMp16(aes, kdfInput, WC_SHE_KEY_SZ * 2, k1);
/* ---- Build M1: UID(15B) | TargetKeyID(4b) | AuthKeyID(4b) ---- */
if (ret == 0) {
XMEMCPY(m1, uid, WC_SHE_UID_SZ);
m1[WC_SHE_M1_KID_OFFSET] =
(byte)((targetKeyId << WC_SHE_M1_KID_SHIFT) |
(authKeyId << WC_SHE_M1_AID_SHIFT));
}
/* ---- Build cleartext M2 and encrypt with K1 ---- */
if (ret == 0) {
/* M2P = m2pHeader(16B) | newKey(16B) */
XMEMCPY(m2, m2pHeader, WC_SHE_KEY_SZ);
XMEMCPY(m2 + WC_SHE_M2_KEY_OFFSET, newKey, WC_SHE_KEY_SZ);
/* Encrypt M2 in-place with AES-128-CBC, IV = 0 */
ret = wc_AesSetKey(aes, k1, WC_SHE_KEY_SZ, NULL, AES_ENCRYPTION);
if (ret == 0) {
ret = wc_AesCbcEncrypt(aes, m2, m2, WC_SHE_M2_SZ);
}
}
/* ---- Derive K2 = AES-MP(AuthKey || CMAC_C) ---- */
if (ret == 0) {
XMEMCPY(kdfInput + WC_SHE_KEY_SZ, macC, WC_SHE_KEY_SZ);
ret = wc_SHE_AesMp16(aes, kdfInput, WC_SHE_KEY_SZ * 2, k2);
}
/* ---- Build M3 = AES-CMAC(K2, M1 || M2) ---- */
if (ret == 0) {
ret = wc_InitCmac_ex(cmac, k2, WC_SHE_KEY_SZ, WC_CMAC_AES,
NULL, she->heap, she->devId);
}
if (ret == 0) {
ret = wc_CmacUpdate(cmac, m1, WC_SHE_M1_SZ);
}
if (ret == 0) {
ret = wc_CmacUpdate(cmac, m2, WC_SHE_M2_SZ);
}
if (ret == 0) {
cmacSz = AES_BLOCK_SIZE;
ret = wc_CmacFinal(cmac, m3, &cmacSz);
}
/* Scrub temporary key material */
ForceZero(k1, sizeof(k1));
ForceZero(k2, sizeof(k2));
ForceZero(kdfInput, sizeof(kdfInput));
wc_AesFree(aes);
WC_FREE_VAR(aes, she->heap);
WC_FREE_VAR(cmac, she->heap);
return ret;
}
/* -------------------------------------------------------------------------- */
/* M4/M5 verification computation */
/* */
/* Derives K3 and K4 from NewKey via Miyaguchi-Preneel, then builds: */
/* M4 = UID | KeyID | AuthID | AES-ECB(K3, counter|pad) */
/* M5 = AES-CMAC(K4, M4) */
/* */
/* These are the expected proof messages that SHE hardware should return. */
/* */
/* When a crypto callback is registered and the SHE context has a valid */
/* device ID, the callback is tried first. This is useful for uploading */
/* M1/M2/M3 to an HSM which loads the key and returns M4/M5 as proof. */
/* If the callback returns CRYPTOCB_UNAVAILABLE, the software path runs. */
/* -------------------------------------------------------------------------- */
int wc_SHE_GenerateM4M5(wc_SHE* she,
const byte* uid, word32 uidSz,
byte authKeyId, byte targetKeyId,
const byte* newKey, word32 newKeySz,
word32 counter,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz)
{
int ret = 0;
byte m2pHeader[WC_SHE_KEY_SZ];
byte m4pHeader[WC_SHE_KEY_SZ];
byte k3[WC_SHE_KEY_SZ];
byte k4[WC_SHE_KEY_SZ];
byte kdfInput[WC_SHE_KEY_SZ * 2];
byte encC[] = WC_SHE_KEY_UPDATE_ENC_C;
byte macC[] = WC_SHE_KEY_UPDATE_MAC_C;
word32 cmacSz;
WC_DECLARE_VAR(aes, Aes, 1, 0);
WC_DECLARE_VAR(cmac, Cmac, 1, 0);
/* Validate SHE context first */
if (she == NULL) {
return BAD_FUNC_ARG;
}
#ifdef WOLF_CRYPTO_CB
/* Try callback first -- useful for uploading M1/M2/M3 to an HSM which
* loads the key and returns the correct M4/M5 proof values. The callback
* handles its own parameter validation. */
if (she->devId != INVALID_DEVID) {
ret = wc_CryptoCb_SheGenerateM4M5(she, uid, uidSz,
authKeyId, targetKeyId,
newKey, newKeySz, counter,
m4, m4Sz, m5, m5Sz);
if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
return ret;
}
/* fall-through to software path */
ret = 0;
}
#endif
/* Software path -- validate all parameters */
if (uid == NULL || uidSz != WC_SHE_UID_SZ ||
newKey == NULL || newKeySz != WC_SHE_KEY_SZ ||
m4 == NULL || m4Sz < WC_SHE_M4_SZ ||
m5 == NULL || m5Sz < WC_SHE_M5_SZ) {
return BAD_FUNC_ARG;
}
/* Override KDF constants if explicitly set */
#ifdef WOLFSSL_SHE_EXTENDED
if (she->kdfEncOverride) {
XMEMCPY(encC, she->kdfEncC, WC_SHE_KEY_SZ);
}
if (she->kdfMacOverride) {
XMEMCPY(macC, she->kdfMacC, WC_SHE_KEY_SZ);
}
#endif
/* Build headers from counter (skipped if overridden) */
she_build_headers(she, counter, 0, m2pHeader, m4pHeader);
WC_ALLOC_VAR(aes, Aes, 1, she->heap);
if (!WC_VAR_OK(aes)) {
return MEMORY_E;
}
WC_ALLOC_VAR(cmac, Cmac, 1, she->heap);
if (!WC_VAR_OK(cmac)) {
WC_FREE_VAR(aes, she->heap);
return MEMORY_E;
}
/* Init AES once -- used by both MP16 and ECB */
ret = wc_AesInit(aes, she->heap, she->devId);
if (ret != 0) {
WC_FREE_VAR(aes, she->heap);
WC_FREE_VAR(cmac, she->heap);
return ret;
}
/* ---- Derive K3 = AES-MP(NewKey || CENC) ---- */
XMEMCPY(kdfInput, newKey, WC_SHE_KEY_SZ);
XMEMCPY(kdfInput + WC_SHE_KEY_SZ, encC, WC_SHE_KEY_SZ);
ret = wc_SHE_AesMp16(aes, kdfInput, WC_SHE_KEY_SZ * 2, k3);
/* ---- Build M4: UID|IDs header + AES-ECB(K3, m4pHeader) ---- */
if (ret == 0) {
XMEMSET(m4, 0, WC_SHE_M4_SZ);
XMEMCPY(m4, uid, WC_SHE_UID_SZ);
m4[WC_SHE_M4_KID_OFFSET] =
(byte)((targetKeyId << WC_SHE_M4_KID_SHIFT) |
(authKeyId << WC_SHE_M4_AID_SHIFT));
/* Copy pre-built M4P header (counter|pad) into M4 counter block */
XMEMCPY(m4 + WC_SHE_M4_COUNT_OFFSET, m4pHeader,
WC_SHE_KEY_SZ);
/* Encrypt the 16-byte counter block in-place with AES-ECB */
ret = wc_AesSetKey(aes, k3, WC_SHE_KEY_SZ, NULL, AES_ENCRYPTION);
if (ret == 0) {
ret = wc_AesEncryptDirect(aes,
m4 + WC_SHE_M4_COUNT_OFFSET,
m4 + WC_SHE_M4_COUNT_OFFSET);
}
}
/* ---- Derive K4 = AES-MP(NewKey || CMAC_C) ---- */
if (ret == 0) {
XMEMCPY(kdfInput + WC_SHE_KEY_SZ, macC, WC_SHE_KEY_SZ);
ret = wc_SHE_AesMp16(aes, kdfInput, WC_SHE_KEY_SZ * 2, k4);
}
/* ---- Build M5 = AES-CMAC(K4, M4) ---- */
if (ret == 0) {
cmacSz = AES_BLOCK_SIZE;
ret = wc_AesCmacGenerate_ex(cmac, m5, &cmacSz,
m4, WC_SHE_M4_SZ, k4, WC_SHE_KEY_SZ,
she->heap, she->devId);
}
ForceZero(k3, sizeof(k3));
ForceZero(k4, sizeof(k4));
ForceZero(kdfInput, sizeof(kdfInput));
wc_AesFree(aes);
WC_FREE_VAR(aes, she->heap);
WC_FREE_VAR(cmac, she->heap);
return ret;
}
/* -------------------------------------------------------------------------- */
/* One-shot Load Key helpers */
/* */
/* Internal helper that does the actual work: imports M1/M2/M3 into the */
/* already-initialized SHE context, calls GenerateM4M5 (which dispatches to */
/* the crypto callback to send M1/M2/M3 to the HSM and receive M4/M5 back), */
/* and frees the context. */
/* -------------------------------------------------------------------------- */
#ifndef NO_WC_SHE_LOADKEY
#if defined(WOLF_CRYPTO_CB) && !defined(NO_WC_SHE_IMPORT_M123)
static int wc_SHE_LoadKey_Internal(wc_SHE* she,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz)
{
int ret;
ret = wc_SHE_ImportM1M2M3(she, m1, m1Sz, m2, m2Sz, m3, m3Sz);
if (ret != 0) {
wc_SHE_Free(she);
return ret;
}
/* GenerateM4M5 with NULL uid/newKey -- the callback reads M1/M2/M3
* from the context and sends them to the HSM which returns M4/M5. */
ret = wc_SHE_GenerateM4M5(she, NULL, 0, 0, 0, NULL, 0, 0,
m4, m4Sz, m5, m5Sz);
wc_SHE_Free(she);
return ret;
}
/* -------------------------------------------------------------------------- */
/* wc_SHE_LoadKey */
/* */
/* One-shot: Init, ImportM1M2M3, GenerateM4M5 (via callback), Free. */
/* Requires a valid devId (not INVALID_DEVID) since the operation dispatches */
/* to a hardware crypto callback. */
/* -------------------------------------------------------------------------- */
int wc_SHE_LoadKey(
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz)
{
int ret;
WC_DECLARE_VAR(she, wc_SHE, 1, heap);
if (m1 == NULL || m2 == NULL || m3 == NULL ||
m4 == NULL || m5 == NULL) {
return BAD_FUNC_ARG;
}
if (devId == INVALID_DEVID) {
return BAD_FUNC_ARG;
}
if (m1Sz != WC_SHE_M1_SZ || m2Sz != WC_SHE_M2_SZ ||
m3Sz != WC_SHE_M3_SZ) {
return BAD_FUNC_ARG;
}
if (m4Sz < WC_SHE_M4_SZ || m5Sz < WC_SHE_M5_SZ) {
return BAD_FUNC_ARG;
}
WC_ALLOC_VAR(she, wc_SHE, 1, heap);
if (!WC_VAR_OK(she)) {
return MEMORY_E;
}
ret = wc_SHE_Init(she, heap, devId);
if (ret != 0) {
WC_FREE_VAR(she, heap);
return ret;
}
ret = wc_SHE_LoadKey_Internal(she, m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
WC_FREE_VAR(she, heap);
return ret;
}
#ifdef WOLF_PRIVATE_KEY_ID
/* -------------------------------------------------------------------------- */
/* wc_SHE_LoadKey_Id */
/* */
/* One-shot with opaque hardware key identifier. */
/* Requires a valid devId (not INVALID_DEVID) since the operation dispatches */
/* to a hardware crypto callback. */
/* -------------------------------------------------------------------------- */
int wc_SHE_LoadKey_Id(
unsigned char* id, int idLen,
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz)
{
int ret;
WC_DECLARE_VAR(she, wc_SHE, 1, heap);
if (id == NULL || m1 == NULL || m2 == NULL || m3 == NULL ||
m4 == NULL || m5 == NULL) {
return BAD_FUNC_ARG;
}
if (devId == INVALID_DEVID) {
return BAD_FUNC_ARG;
}
if (idLen < 0 || idLen > WC_SHE_MAX_ID_LEN) {
return BAD_FUNC_ARG;
}
if (m1Sz != WC_SHE_M1_SZ || m2Sz != WC_SHE_M2_SZ ||
m3Sz != WC_SHE_M3_SZ) {
return BAD_FUNC_ARG;
}
if (m4Sz < WC_SHE_M4_SZ || m5Sz < WC_SHE_M5_SZ) {
return BAD_FUNC_ARG;
}
WC_ALLOC_VAR(she, wc_SHE, 1, heap);
if (!WC_VAR_OK(she)) {
return MEMORY_E;
}
ret = wc_SHE_Init_Id(she, id, idLen, heap, devId);
if (ret != 0) {
WC_FREE_VAR(she, heap);
return ret;
}
ret = wc_SHE_LoadKey_Internal(she, m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
WC_FREE_VAR(she, heap);
return ret;
}
/* -------------------------------------------------------------------------- */
/* wc_SHE_LoadKey_Label */
/* */
/* One-shot with human-readable key label. */
/* Requires a valid devId (not INVALID_DEVID) since the operation dispatches */
/* to a hardware crypto callback. */
/* -------------------------------------------------------------------------- */
int wc_SHE_LoadKey_Label(
const char* label,
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz)
{
int ret;
word32 labelLen;
WC_DECLARE_VAR(she, wc_SHE, 1, heap);
if (label == NULL || m1 == NULL || m2 == NULL || m3 == NULL ||
m4 == NULL || m5 == NULL) {
return BAD_FUNC_ARG;
}
if (devId == INVALID_DEVID) {
return BAD_FUNC_ARG;
}
labelLen = (word32)XSTRLEN(label);
if (labelLen == 0 || labelLen > WC_SHE_MAX_LABEL_LEN) {
return BAD_FUNC_ARG;
}
if (m1Sz != WC_SHE_M1_SZ || m2Sz != WC_SHE_M2_SZ ||
m3Sz != WC_SHE_M3_SZ) {
return BAD_FUNC_ARG;
}
if (m4Sz < WC_SHE_M4_SZ || m5Sz < WC_SHE_M5_SZ) {
return BAD_FUNC_ARG;
}
WC_ALLOC_VAR(she, wc_SHE, 1, heap);
if (!WC_VAR_OK(she)) {
return MEMORY_E;
}
ret = wc_SHE_Init_Label(she, label, heap, devId);
if (ret != 0) {
WC_FREE_VAR(she, heap);
return ret;
}
ret = wc_SHE_LoadKey_Internal(she, m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
WC_FREE_VAR(she, heap);
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID */
/* -------------------------------------------------------------------------- */
/* One-shot Load Key with Verification */
/* */
/* Same as the LoadKey variants but also compares the M4/M5 returned by the */
/* HSM against caller-provided expected values. Returns SIG_VERIFY_E on */
/* mismatch. The actual M4/M5 from the HSM are still written to the output */
/* buffers so the caller can inspect them on failure. */
/* -------------------------------------------------------------------------- */
static int wc_SHE_VerifyM4M5(
const byte* m4, word32 m4Sz,
const byte* m5, word32 m5Sz,
const byte* m4Expected, word32 m4ExpectedSz,
const byte* m5Expected, word32 m5ExpectedSz)
{
if (m4Expected == NULL || m5Expected == NULL) {
return BAD_FUNC_ARG;
}
if (m4ExpectedSz != WC_SHE_M4_SZ || m5ExpectedSz != WC_SHE_M5_SZ ||
m4Sz < WC_SHE_M4_SZ || m5Sz < WC_SHE_M5_SZ) {
return BAD_FUNC_ARG;
}
if (ConstantCompare(m4, m4Expected, WC_SHE_M4_SZ) != 0 ||
ConstantCompare(m5, m5Expected, WC_SHE_M5_SZ) != 0) {
return SIG_VERIFY_E;
}
return 0;
}
int wc_SHE_LoadKey_Verify(
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz,
const byte* m4Expected, word32 m4ExpectedSz,
const byte* m5Expected, word32 m5ExpectedSz)
{
int ret;
ret = wc_SHE_LoadKey(heap, devId, m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
if (ret != 0) {
return ret;
}
return wc_SHE_VerifyM4M5(m4, m4Sz, m5, m5Sz,
m4Expected, m4ExpectedSz,
m5Expected, m5ExpectedSz);
}
#ifdef WOLF_PRIVATE_KEY_ID
int wc_SHE_LoadKey_Verify_Id(
unsigned char* id, int idLen,
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz,
const byte* m4Expected, word32 m4ExpectedSz,
const byte* m5Expected, word32 m5ExpectedSz)
{
int ret;
ret = wc_SHE_LoadKey_Id(id, idLen, heap, devId,
m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
if (ret != 0) {
return ret;
}
return wc_SHE_VerifyM4M5(m4, m4Sz, m5, m5Sz,
m4Expected, m4ExpectedSz,
m5Expected, m5ExpectedSz);
}
int wc_SHE_LoadKey_Verify_Label(
const char* label,
void* heap, int devId,
const byte* m1, word32 m1Sz,
const byte* m2, word32 m2Sz,
const byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz,
const byte* m4Expected, word32 m4ExpectedSz,
const byte* m5Expected, word32 m5ExpectedSz)
{
int ret;
ret = wc_SHE_LoadKey_Label(label, heap, devId,
m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz);
if (ret != 0) {
return ret;
}
return wc_SHE_VerifyM4M5(m4, m4Sz, m5, m5Sz,
m4Expected, m4ExpectedSz,
m5Expected, m5ExpectedSz);
}
#endif /* WOLF_PRIVATE_KEY_ID */
#endif /* WOLF_CRYPTO_CB && !NO_WC_SHE_IMPORT_M123 */
#endif /* !NO_WC_SHE_LOADKEY */
/* -------------------------------------------------------------------------- */
/* Export Key */
/* */
/* When a crypto callback is registered, it can be used to export M1-M5 */
/* from a key slot on an HSM, allowing the key to be re-loaded later via */
/* the SHE key update protocol. */
/* Any pointer may be NULL to skip that message. */
/* -------------------------------------------------------------------------- */
#if defined(WOLF_CRYPTO_CB) && !defined(NO_WC_SHE_EXPORTKEY)
int wc_SHE_ExportKey(wc_SHE* she,
byte* m1, word32 m1Sz,
byte* m2, word32 m2Sz,
byte* m3, word32 m3Sz,
byte* m4, word32 m4Sz,
byte* m5, word32 m5Sz,
const void* ctx)
{
if (she == NULL) {
return BAD_FUNC_ARG;
}
return wc_CryptoCb_SheExportKey(she,
m1, m1Sz, m2, m2Sz, m3, m3Sz,
m4, m4Sz, m5, m5Sz, ctx);
}
#endif /* WOLF_CRYPTO_CB && !NO_WC_SHE_EXPORTKEY */
#endif /* WOLFSSL_SHE */