Skip to content

Commit 148ef5b

Browse files
Avoid writing into caller ikm buffer in wc_Tls13_HKDF_Extract
1 parent 2c5e135 commit 148ef5b

5 files changed

Lines changed: 202 additions & 40 deletions

File tree

doc/dox_comments/header_files-ja/hmac.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -333,25 +333,25 @@ int wc_HKDF_Expand_ex(
333333
\brief この関数は、TLS v1.3鍵導出のためのRFC 5869 HMACベース抽出拡張鍵導出関数(HKDF)へのアクセスを提供します。
334334
335335
\return 0 指定された入力で鍵の生成に成功した場合に返されます。
336-
\return BAD_FUNC_ARG 無効なハッシュタイプが指定された場合に返されます(typeパラメータを参照)
336+
\return BAD_FUNC_ARG 無効なハッシュタイプが指定された場合(digestパラメータを参照)、prkがNULLの場合、またはikmがNULLでikmLenが0でない場合に返されます
337337
\return MEMORY_E メモリ割り当てエラーがある場合に返されます。
338338
\return HMAC_MIN_KEYLEN_E FIPS実装を使用していて、指定された鍵の長さが最小許容FIPS標準より短い場合に返される可能性があります。
339339
340340
\param prk 生成された疑似ランダム鍵。
341-
\param salt ソルト。
341+
\param salt ソルト。NULLの場合、saltLenは無視されます。
342342
\param saltLen ソルトの長さ。
343-
\param ikm 鍵材料の出力へのポインタ
344-
\param ikmLen 入力鍵材料バッファの長さ
343+
\param ikm 入力鍵材料。ikmLenが0の場合はNULLにできます
344+
\param ikmLen 入力鍵材料の長さ。0の場合、RFC 8446に従い、空の入力鍵材料ではなくダイジェスト長のゼロ値が使用されます
345345
\param digest HKDFに使用するハッシュタイプ。有効なタイプは:WC_SHA256、WC_SHA384、またはWC_SHA512。
346346
347347
_Example_
348348
\code
349-
byte secret[] = { // ランダム鍵で初期化 };
349+
byte ikm[] = { // 入力鍵材料で初期化 };
350350
byte salt[] = { // オプションのソルトで初期化 };
351-
byte masterSecret[MAX_DIGEST_SIZE];
351+
byte prk[MAX_DIGEST_SIZE];
352352
353-
int ret = wc_Tls13_HKDF_Extract(secret, salt, sizeof(salt), 0,
354-
masterSecret, sizeof(masterSecret), WC_SHA512);
353+
int ret = wc_Tls13_HKDF_Extract(prk, salt, sizeof(salt), ikm, sizeof(ikm),
354+
WC_SHA512);
355355
if ( ret != 0 ) {
356356
// 導出鍵の生成エラー
357357
}
@@ -374,27 +374,27 @@ int wc_Tls13_HKDF_Extract(
374374
\brief この関数は、TLS v1.3鍵導出のためのRFC 5869 HMACベース抽出拡張鍵導出関数(HKDF)へのアクセスを提供します。これは、ヒープヒントとデバイス識別子を追加する_exバージョンです。
375375
376376
\return 0 指定された入力で鍵の生成に成功した場合に返されます。
377-
\return BAD_FUNC_ARG 無効なハッシュタイプが指定された場合に返されます(typeパラメータを参照)
377+
\return BAD_FUNC_ARG 無効なハッシュタイプが指定された場合(digestパラメータを参照)、prkがNULLの場合、またはikmがNULLでikmLenが0でない場合に返されます
378378
\return MEMORY_E メモリ割り当てエラーがある場合に返されます。
379379
\return HMAC_MIN_KEYLEN_E FIPS実装を使用していて、指定された鍵の長さが最小許容FIPS標準より短い場合に返される可能性があります。
380380
381381
\param prk 生成された疑似ランダム鍵。
382-
\param salt ソルト。
382+
\param salt ソルト。NULLにできます。その場合、暗号コールバックが処理しない限りsaltLenは無視されます。
383383
\param saltLen ソルトの長さ。
384-
\param ikm 鍵材料の出力へのポインタ
385-
\param ikmLen 入力鍵材料バッファの長さ
384+
\param ikm 入力鍵材料。ikmLenが0の場合はNULLにできます
385+
\param ikmLen 入力鍵材料の長さ。0の場合、RFC 8446に従い、空の入力鍵材料ではなくダイジェスト長のゼロ値が使用されます
386386
\param digest HKDFに使用するハッシュタイプ。有効なタイプは:WC_SHA256、WC_SHA384、またはWC_SHA512。
387387
\param heap メモリに使用するヒープヒント。NULLにできます。
388388
\param devId 暗号コールバックまたは非同期ハードウェアで使用するID。使用しない場合はINVALID_DEVID(-2)に設定します。
389389
390390
_Example_
391391
\code
392-
byte secret[] = { // ランダム鍵で初期化 };
392+
byte ikm[] = { // 入力鍵材料で初期化 };
393393
byte salt[] = { // オプションのソルトで初期化 };
394-
byte masterSecret[MAX_DIGEST_SIZE];
394+
byte prk[MAX_DIGEST_SIZE];
395395
396-
int ret = wc_Tls13_HKDF_Extract_ex(secret, salt, sizeof(salt), 0,
397-
masterSecret, sizeof(masterSecret), WC_SHA512, NULL, INVALID_DEVID);
396+
int ret = wc_Tls13_HKDF_Extract_ex(prk, salt, sizeof(salt), ikm,
397+
sizeof(ikm), WC_SHA512, NULL, INVALID_DEVID);
398398
if ( ret != 0 ) {
399399
// 導出鍵の生成エラー
400400
}

doc/dox_comments/header_files/hmac.h

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -405,27 +405,29 @@ int wc_HKDF_Expand_ex(
405405
key derivation
406406
407407
\return 0 Returned upon successfully generating a key with the given inputs
408-
\return BAD_FUNC_ARG Returned if an invalid hash type is given (see type param)
408+
\return BAD_FUNC_ARG Returned if an invalid hash type is given (see digest
409+
param), if prk is NULL, or if ikm is NULL and ikmLen is not 0
409410
\return MEMORY_E Returned if there is an error allocating memory
410411
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
411412
and the key length specified is shorter than the minimum acceptable FIPS
412413
standard
413414
414415
\param prk Generated pseudorandom key
415-
\param salt salt.
416+
\param salt salt. May be NULL, in which case saltLen is ignored
416417
\param saltLen length of the salt
417-
\param ikm pointer to putput for keying material
418-
\param ikmLen length of the input keying material buffer
418+
\param ikm input keying material. May be NULL when ikmLen is 0
419+
\param ikmLen length of the input keying material. 0 substitutes a digest
420+
length zeroed IKM per RFC 8446, not an empty IKM
419421
\param digest hash type to use for the HKDF. Valid types are: WC_SHA256, WC_SHA384 or WC_SHA512
420422
421423
_Example_
422424
\code
423-
byte secret[] = { // initialize with random key };
425+
byte ikm[] = { // initialize with input keying material };
424426
byte salt[] = { // initialize with optional salt };
425-
byte masterSecret[MAX_DIGEST_SIZE];
427+
byte prk[MAX_DIGEST_SIZE];
426428
427-
int ret = wc_Tls13_HKDF_Extract(secret, salt, sizeof(salt), 0,
428-
masterSecret, sizeof(masterSecret), WC_SHA512);
429+
int ret = wc_Tls13_HKDF_Extract(prk, salt, sizeof(salt), ikm, sizeof(ikm),
430+
WC_SHA512);
429431
if ( ret != 0 ) {
430432
// error generating derived key
431433
}
@@ -450,29 +452,32 @@ int wc_Tls13_HKDF_Extract(
450452
key derivation. This is the _ex version adding heap hint and device identifier.
451453
452454
\return 0 Returned upon successfully generating a key with the given inputs
453-
\return BAD_FUNC_ARG Returned if an invalid hash type is given (see type param)
455+
\return BAD_FUNC_ARG Returned if an invalid hash type is given (see digest
456+
param), if prk is NULL, or if ikm is NULL and ikmLen is not 0
454457
\return MEMORY_E Returned if there is an error allocating memory
455458
\return HMAC_MIN_KEYLEN_E May be returned when using a FIPS implementation
456459
and the key length specified is shorter than the minimum acceptable FIPS
457460
standard
458461
459462
\param prk Generated pseudorandom key
460-
\param salt Salt.
463+
\param salt Salt. May be NULL; saltLen is then ignored unless a crypto
464+
callback handles the request
461465
\param saltLen Length of the salt
462-
\param ikm Pointer to output for keying material
463-
\param ikmLen Length of the input keying material buffer
466+
\param ikm Input keying material. May be NULL when ikmLen is 0
467+
\param ikmLen Length of the input keying material. 0 substitutes a digest
468+
length zeroed IKM per RFC 8446, not an empty IKM
464469
\param digest Hash type to use for the HKDF. Valid types are: WC_SHA256, WC_SHA384 or WC_SHA512
465470
\param heap Heap hint to use for memory. Can be NULL
466471
\param devId ID to use with crypto callbacks or async hardware. Set to INVALID_DEVID (-2) if not used
467472
468473
_Example_
469474
\code
470-
byte secret[] = { // initialize with random key };
475+
byte ikm[] = { // initialize with input keying material };
471476
byte salt[] = { // initialize with optional salt };
472-
byte masterSecret[MAX_DIGEST_SIZE];
477+
byte prk[MAX_DIGEST_SIZE];
473478
474-
int ret = wc_Tls13_HKDF_Extract_ex(secret, salt, sizeof(salt), 0,
475-
masterSecret, sizeof(masterSecret), WC_SHA512, NULL, INVALID_DEVID);
479+
int ret = wc_Tls13_HKDF_Extract_ex(prk, salt, sizeof(salt), ikm,
480+
sizeof(ikm), WC_SHA512, NULL, INVALID_DEVID);
476481
if ( ret != 0 ) {
477482
// error generating derived key
478483
}

tests/api/test_hmac.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#endif
3030

3131
#include <wolfssl/wolfcrypt/hmac.h>
32+
#include <wolfssl/wolfcrypt/kdf.h>
3233
#include <wolfssl/wolfcrypt/types.h>
3334
#include <wolfssl/internal.h>
3435
#ifdef WOLF_CRYPTO_CB
@@ -1057,3 +1058,148 @@ int test_wc_HKDF_NullKeyEdgeCases(void)
10571058
#endif
10581059
return EXPECT_RESULT();
10591060
} /* END test_wc_HKDF_NullKeyEdgeCases */
1061+
1062+
#if defined(HAVE_HKDF) && !defined(NO_HMAC) && !defined(NO_KDF) && \
1063+
!defined(NO_SHA256) && !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \
1064+
defined(WOLF_CRYPTO_CB)
1065+
#define TEST_KDF_CRYPTOCB_DEVID 0x4b444658 /* "KDFX" */
1066+
1067+
typedef struct {
1068+
int called;
1069+
int inKeyNull;
1070+
int inKeyZeroed;
1071+
word32 inKeySz;
1072+
} TestKdfExtractCbCtx;
1073+
1074+
/* Records the IKM handed to a callback, then declines so the software path
1075+
* still derives the PRK. */
1076+
static int test_kdf_cryptocb_extract_cb(int cbDevId, wc_CryptoInfo* info,
1077+
void* ctx)
1078+
{
1079+
TestKdfExtractCbCtx* cbCtx = (TestKdfExtractCbCtx*)ctx;
1080+
word32 j;
1081+
1082+
(void)cbDevId;
1083+
if (info->algo_type == WC_ALGO_TYPE_KDF &&
1084+
info->kdf.type == WC_KDF_TYPE_HKDF_EXTRACT) {
1085+
cbCtx->called++;
1086+
cbCtx->inKeySz = info->kdf.hkdf_extract.inKeySz;
1087+
cbCtx->inKeyNull = (info->kdf.hkdf_extract.inKey == NULL);
1088+
cbCtx->inKeyZeroed = 1;
1089+
for (j = 0; !cbCtx->inKeyNull && j < info->kdf.hkdf_extract.inKeySz;
1090+
j++) {
1091+
if (info->kdf.hkdf_extract.inKey[j] != 0) {
1092+
cbCtx->inKeyZeroed = 0;
1093+
}
1094+
}
1095+
}
1096+
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
1097+
}
1098+
#endif
1099+
1100+
/* A zero length IKM must not write into the caller's ikm buffer, whose
1101+
* required size is not implied by the length the caller passes in. */
1102+
int test_wc_Tls13_HKDF_Extract_ZeroLenIkm(void)
1103+
{
1104+
EXPECT_DECLS;
1105+
/* kdf.c sits inside the FIPS module boundary, so FIPS and selftest builds use a
1106+
* frozen copy that still writes into the caller's ikm buffer. Skip until a
1107+
* module revision carrying this fix ships. */
1108+
#if defined(HAVE_HKDF) && !defined(NO_HMAC) && !defined(NO_KDF) && \
1109+
!defined(NO_SHA256) && !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS)
1110+
static const int digests[] = {
1111+
WC_SHA256,
1112+
#ifdef WOLFSSL_SHA384
1113+
WC_SHA384,
1114+
#endif
1115+
#ifdef WOLFSSL_TLS13_SHA512
1116+
WC_SHA512,
1117+
#endif
1118+
#ifdef WOLFSSL_SM3
1119+
WC_SM3,
1120+
#endif
1121+
};
1122+
byte prkNull[WC_MAX_DIGEST_SIZE];
1123+
byte prkOther[WC_MAX_DIGEST_SIZE];
1124+
byte zeros[WC_MAX_DIGEST_SIZE];
1125+
byte sentinel[WC_MAX_DIGEST_SIZE];
1126+
byte filled[WC_MAX_DIGEST_SIZE];
1127+
byte small[4];
1128+
word32 i;
1129+
int len = 0;
1130+
#ifdef WOLF_CRYPTO_CB
1131+
TestKdfExtractCbCtx cbCtx;
1132+
#endif
1133+
1134+
XMEMSET(zeros, 0, sizeof(zeros));
1135+
XMEMSET(filled, 0xA5, sizeof(filled));
1136+
1137+
/* Every digest the switch accepts, up to the WC_MAX_DIGEST_SIZE the scratch
1138+
* buffer is sized against. */
1139+
for (i = 0; i < sizeof(digests) / sizeof(digests[0]); i++) {
1140+
ExpectIntGT(len = wc_HmacSizeByType(digests[i]), 0);
1141+
1142+
/* ikmLen 0 feeds a digest length zeroed IKM rather than an empty one,
1143+
* so a NULL ikm must derive the same PRK as passing those zeros. */
1144+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkNull, NULL, 0, NULL, 0,
1145+
digests[i]), 0);
1146+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkOther, NULL, 0, zeros,
1147+
(word32)len, digests[i]), 0);
1148+
ExpectBufEQ(prkNull, prkOther, (word32)len);
1149+
1150+
/* The caller's buffer must come back untouched, through the _ex entry
1151+
* point that TLS 1.3 itself calls. */
1152+
XMEMSET(sentinel, 0xA5, sizeof(sentinel));
1153+
ExpectIntEQ(wc_Tls13_HKDF_Extract_ex(prkOther, NULL, 0, sentinel, 0,
1154+
digests[i], NULL, INVALID_DEVID), 0);
1155+
ExpectBufEQ(sentinel, filled, (word32)len);
1156+
ExpectBufEQ(prkNull, prkOther, (word32)len);
1157+
1158+
XMEMSET(sentinel, 0xA5, sizeof(sentinel));
1159+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkOther, NULL, 0, sentinel, 0,
1160+
digests[i]), 0);
1161+
ExpectBufEQ(sentinel, filled, (word32)len);
1162+
ExpectBufEQ(prkNull, prkOther, (word32)len);
1163+
1164+
/* A NULL salt stays valid whatever saltLen says. */
1165+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkOther, NULL, 5, NULL, 0,
1166+
digests[i]), 0);
1167+
ExpectBufEQ(prkNull, prkOther, (word32)len);
1168+
}
1169+
1170+
/* ASan tripwire: a revert overruns this and may abort the binary under a
1171+
* stack protector. The sentinel above is what fails cleanly. */
1172+
XMEMSET(small, 0xA5, sizeof(small));
1173+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkNull, NULL, 0, small, 0, WC_SHA256), 0);
1174+
ExpectIntEQ(small[0], 0xA5);
1175+
ExpectIntEQ(small[3], 0xA5);
1176+
1177+
ExpectIntEQ(wc_Tls13_HKDF_Extract(NULL, NULL, 0, zeros,
1178+
WC_SHA256_DIGEST_SIZE, WC_SHA256), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
1179+
ExpectIntEQ(wc_Tls13_HKDF_Extract(prkNull, NULL, 0, NULL, 5, WC_SHA256),
1180+
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
1181+
ExpectIntEQ(wc_Tls13_HKDF_Extract_ex(NULL, NULL, 0, zeros,
1182+
WC_SHA256_DIGEST_SIZE, WC_SHA256, NULL, INVALID_DEVID),
1183+
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
1184+
ExpectIntEQ(wc_Tls13_HKDF_Extract_ex(prkNull, NULL, 0, NULL, 5, WC_SHA256,
1185+
NULL, INVALID_DEVID), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
1186+
1187+
#ifdef WOLF_CRYPTO_CB
1188+
/* A callback dispatches before the software path, so it must receive the
1189+
* substituted zeroed IKM rather than the caller's untouched buffer. */
1190+
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
1191+
XMEMSET(sentinel, 0xA5, sizeof(sentinel));
1192+
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_KDF_CRYPTOCB_DEVID,
1193+
test_kdf_cryptocb_extract_cb, &cbCtx), 0);
1194+
ExpectIntEQ(wc_Tls13_HKDF_Extract_ex(prkOther, NULL, 0, sentinel, 0,
1195+
WC_SHA256, NULL, TEST_KDF_CRYPTOCB_DEVID), 0);
1196+
ExpectIntEQ(cbCtx.called, 1);
1197+
ExpectIntEQ(cbCtx.inKeyNull, 0);
1198+
ExpectIntEQ(cbCtx.inKeyZeroed, 1);
1199+
ExpectIntEQ(cbCtx.inKeySz, WC_SHA256_DIGEST_SIZE);
1200+
ExpectBufEQ(sentinel, filled, WC_SHA256_DIGEST_SIZE);
1201+
wc_CryptoCb_UnRegisterDevice(TEST_KDF_CRYPTOCB_DEVID);
1202+
#endif
1203+
#endif
1204+
return EXPECT_RESULT();
1205+
} /* END test_wc_Tls13_HKDF_Extract_ZeroLenIkm */

tests/api/test_hmac.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ int test_wc_HmacInit_Id(void);
4646
int test_wc_HmacInit_Label(void);
4747
int test_wc_HmacFree_CryptoCb(void);
4848
int test_wc_HKDF_NullKeyEdgeCases(void);
49+
int test_wc_Tls13_HKDF_Extract_ZeroLenIkm(void);
4950

5051
#define TEST_HMAC_DECLS \
5152
TEST_DECL_GROUP("hmac", test_wc_Md5HmacSetKey), \
@@ -69,6 +70,7 @@ int test_wc_HKDF_NullKeyEdgeCases(void);
6970
TEST_DECL_GROUP("hmac", test_wc_HmacInit_Id), \
7071
TEST_DECL_GROUP("hmac", test_wc_HmacInit_Label), \
7172
TEST_DECL_GROUP("hmac", test_wc_HmacFree_CryptoCb), \
72-
TEST_DECL_GROUP("hmac", test_wc_HKDF_NullKeyEdgeCases)
73+
TEST_DECL_GROUP("hmac", test_wc_HKDF_NullKeyEdgeCases), \
74+
TEST_DECL_GROUP("hmac", test_wc_Tls13_HKDF_Extract_ZeroLenIkm)
7375

7476
#endif /* WOLFCRYPT_TEST_HMAC_H */

wolfcrypt/src/kdf.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,15 @@ int wc_PRF_TLS(byte* digest, word32 digLen, const byte* secret, word32 secLen,
348348
int wc_Tls13_HKDF_Extract_ex(byte* prk, const byte* salt, word32 saltLen,
349349
byte* ikm, word32 ikmLen, int digest, void* heap, int devId)
350350
{
351-
int ret;
351+
byte tmp[WC_MAX_DIGEST_SIZE]; /* localIkm helper */
352+
const byte* localIkm; /* either points to user input or tmp */
353+
int ret;
352354
word32 len = 0;
353355

356+
if (prk == NULL || (ikm == NULL && ikmLen > 0)) {
357+
return BAD_FUNC_ARG;
358+
}
359+
354360
switch (digest) {
355361
#ifndef NO_SHA256
356362
case WC_SHA256:
@@ -380,25 +386,28 @@ int wc_PRF_TLS(byte* digest, word32 digLen, const byte* secret, word32 secLen,
380386
return BAD_FUNC_ARG;
381387
}
382388

383-
/* When length is 0 then use zeroed data of digest length. */
389+
/* When length is 0 then use zeroed data of digest length. The caller's
390+
* buffer is not sized for this, so use a local one. */
391+
localIkm = ikm;
384392
if (ikmLen == 0) {
393+
XMEMSET(tmp, 0, len);
394+
localIkm = tmp;
385395
ikmLen = len;
386-
XMEMSET(ikm, 0, len);
387396
}
388397

389398
#ifdef WOLFSSL_DEBUG_TLS
390399
WOLFSSL_MSG(" Salt");
391400
WOLFSSL_BUFFER(salt, saltLen);
392401
WOLFSSL_MSG(" IKM");
393-
WOLFSSL_BUFFER(ikm, ikmLen);
402+
WOLFSSL_BUFFER(localIkm, ikmLen);
394403
#endif
395404

396405
#if !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || \
397406
(defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
398-
ret = wc_HKDF_Extract_ex(digest, salt, saltLen, ikm, ikmLen, prk, heap,
399-
devId);
407+
ret = wc_HKDF_Extract_ex(digest, salt, saltLen, localIkm, ikmLen, prk,
408+
heap, devId);
400409
#else
401-
ret = wc_HKDF_Extract(digest, salt, saltLen, ikm, ikmLen, prk);
410+
ret = wc_HKDF_Extract(digest, salt, saltLen, localIkm, ikmLen, prk);
402411
(void)heap;
403412
(void)devId;
404413
#endif

0 commit comments

Comments
 (0)