Skip to content

Commit 5306a85

Browse files
committed
wolfcrypt: support NIST 800-56C Option 1 KDF
1 parent 200f309 commit 5306a85

4 files changed

Lines changed: 488 additions & 0 deletions

File tree

doc/dox_comments/header_files/kdf.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,39 @@ int wc_SRTP_KDF_label(const byte* key, word32 keySz, const byte* salt,
223223
*/
224224
int wc_SRTP_KDF_kdr_to_idx(word32 kdr);
225225

226+
/**
227+
* \brief Performs the single-step key derivation function (KDF) as specified in
228+
* SP800-56C option 1.
229+
*
230+
* \param [in] z The input keying material.
231+
* \param [in] zSz The size of the input keying material.
232+
* \param [in] fixedInfo The fixed information to be included in the KDF.
233+
* \param [in] fixedInfoSz The size of the fixed information.
234+
* \param [in] derivedSecretSz The desired size of the derived secret.
235+
* \param [in] hashType The hash algorithm to be used in the KDF.
236+
* \param [out] output The buffer to store the derived secret.
237+
* \param [in] outputSz The size of the output buffer.
238+
*
239+
240+
* \return 0 if the KDF operation is successful,
241+
* \return BAD_FUNC_ARG if the input parameters are invalid.
242+
* \return negative error code if the KDF operation fails.
243+
*
244+
* _Example_
245+
\code
246+
unsigned char z[32] = { ... };
247+
unsigned char fixedInfo[16] = { ... };
248+
unsigned char output[32];
249+
int ret;
250+
251+
ret = wc_SP80056C_KDF_single(z, sizeof(z), fixedInfo, sizeof(fixedInfo),
252+
sizeof(output), WC_HASH_TYPE_SHA256, output, sizeof(output));
253+
if (ret != 0) {
254+
WOLFSSL_MSG("wc_SP80056C_KDF_single failed");
255+
}
256+
\endcode
257+
*/
258+
int wc_SP80056C_KDF_single(const byte* z, word32 zSz,
259+
const byte* fixedInfo, word32 fixedInfoSz, word32 derivedSecretSz,
260+
enum wc_HashType hashType, byte* output, word32 outputSz);
261+

wolfcrypt/src/kdf.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,4 +1389,123 @@ int wc_SRTP_KDF_kdr_to_idx(word32 kdr)
13891389
}
13901390
#endif /* WC_SRTP_KDF */
13911391

1392+
#ifdef WC_KDF_NIST_SP_800_56C
1393+
static int wc_SP80056C_KDF_iteration(const byte* z, word32 zSz,
1394+
word32 counter, const byte* fixedInfo, word32 fixedInfoSz,
1395+
enum wc_HashType hashType, byte* output)
1396+
{
1397+
byte counterBuf[4];
1398+
wc_HashAlg hash;
1399+
int ret;
1400+
1401+
ret = wc_HashInit(&hash, hashType);
1402+
if (ret != 0)
1403+
return ret;
1404+
c32toa(counter, counterBuf);
1405+
ret = wc_HashUpdate(&hash, hashType, counterBuf, 4);
1406+
if (ret == 0) {
1407+
ret = wc_HashUpdate(&hash, hashType, z, zSz);
1408+
}
1409+
if (ret == 0 && fixedInfoSz > 0) {
1410+
ret = wc_HashUpdate(&hash, hashType, fixedInfo, fixedInfoSz);
1411+
}
1412+
if (ret == 0) {
1413+
ret = wc_HashFinal(&hash, hashType, output);
1414+
}
1415+
wc_HashFree(&hash, hashType);
1416+
return ret;
1417+
}
1418+
1419+
/**
1420+
* \brief Performs the single-step key derivation function (KDF) as specified in
1421+
* SP800-56C option 1.
1422+
*
1423+
* \param [in] z The input keying material.
1424+
* \param [in] zSz The size of the input keying material.
1425+
* \param [in] fixedInfo The fixed information to be included in the KDF.
1426+
* \param [in] fixedInfoSz The size of the fixed information.
1427+
* \param [in] derivedSecretSz The desired size of the derived secret.
1428+
* \param [in] hashType The hash algorithm to be used in the KDF.
1429+
* \param [out] output The buffer to store the derived secret.
1430+
* \param [in] outputSz The size of the output buffer.
1431+
*
1432+
* \return 0 if the KDF operation is successful.
1433+
* \return BAD_FUNC_ARG if the input parameters are invalid.
1434+
* \return negative error code if the KDF operation fails.
1435+
*/
1436+
int wc_SP80056C_KDF_single(const byte* z, word32 zSz,
1437+
const byte* fixedInfo, word32 fixedInfoSz, word32 derivedSecretSz,
1438+
enum wc_HashType hashType, byte* output, word32 outputSz)
1439+
{
1440+
byte hashTempBuf[WC_MAX_DIGEST_SIZE];
1441+
int ret = BAD_FUNC_ARG;
1442+
word32 counter, outIdx;
1443+
word32 inputSz;
1444+
byte* hashOut;
1445+
int hashOutSz;
1446+
word32 reps;
1447+
1448+
if (output == NULL || outputSz < derivedSecretSz)
1449+
return BAD_FUNC_ARG;
1450+
if (z == NULL || zSz == 0 || (fixedInfoSz > 0 && fixedInfo == NULL))
1451+
return BAD_FUNC_ARG;
1452+
if (derivedSecretSz == 0)
1453+
return BAD_FUNC_ARG;
1454+
1455+
hashOutSz = wc_HashGetDigestSize(hashType);
1456+
if (hashOutSz == HASH_TYPE_E)
1457+
return BAD_FUNC_ARG;
1458+
1459+
/* According to SP800_56C reps shall not be greater than 2**32-1. This is
1460+
* not possible using word32 integers. The code checks for overflow. */
1461+
reps = derivedSecretSz / hashOutSz;
1462+
if (derivedSecretSz % hashOutSz) {
1463+
if (reps + 1 < reps)
1464+
return BAD_FUNC_ARG;
1465+
reps++;
1466+
}
1467+
1468+
/* According to SP800_56C, table 1, the max input size (max_H_inputBits)
1469+
* depends on the HASH algo. The smaller value in the table is (2**64-1)/8.
1470+
* This is larger than the possible length using word32 integers. The code
1471+
* checks for overflow. */
1472+
inputSz = zSz;
1473+
if (inputSz + 4 < inputSz)
1474+
return BAD_FUNC_ARG;
1475+
inputSz += 4;
1476+
if (inputSz + fixedInfoSz < inputSz)
1477+
return BAD_FUNC_ARG;
1478+
1479+
outIdx = 0;
1480+
for (counter = 1; counter <= reps; counter++) {
1481+
/* If the user provided a buffer output size bigger than the
1482+
* derivedSecretSz then the copy in hashTempBuf can be avoided.
1483+
* Nevertheless, the code conservatively does the copy anyway as the
1484+
* data is sensitive and the user may forget zeroing outputsz bytes
1485+
* instead of derivedSecretsz bytes. */
1486+
if (outIdx + hashOutSz <= derivedSecretSz) {
1487+
hashOut = output + outIdx;
1488+
}
1489+
else {
1490+
hashOut = hashTempBuf;
1491+
}
1492+
ret = wc_SP80056C_KDF_iteration(z, zSz, counter,
1493+
fixedInfo, fixedInfoSz, hashType, hashOut);
1494+
if (hashOut == hashTempBuf) {
1495+
XMEMCPY(output + outIdx, hashTempBuf, derivedSecretSz - outIdx);
1496+
ForceZero(hashTempBuf, sizeof(hashTempBuf));
1497+
}
1498+
if (ret != 0)
1499+
break;
1500+
outIdx += hashOutSz;
1501+
}
1502+
1503+
if (ret != 0) {
1504+
ForceZero(output, derivedSecretSz);
1505+
}
1506+
1507+
return ret;
1508+
}
1509+
#endif /* WC_KDF_NIST_SP_800_56C */
1510+
13921511
#endif /* NO_KDF */

0 commit comments

Comments
 (0)