diff --git a/configure.ac b/configure.ac index 0c3f2268e..227ed80af 100644 --- a/configure.ac +++ b/configure.ac @@ -850,6 +850,18 @@ then fi +# X9.63 KDF +AC_ARG_ENABLE([x963kdf], + [ --enable-x963kdf Enable X9.63 KDF support (default: disabled)], + [ ENABLED_X963KDF=$enableval ], + [ ENABLED_X963KDF=no ] + ) +if test "$ENABLED_X963KDF" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DHAVE_X963_KDF" +fi + + # DSA AC_ARG_ENABLE([dsa], [ --enable-dsa Enable DSA (default: disabled)], @@ -3279,6 +3291,7 @@ echo " * Hash DRBG: $ENABLED_HASHDRBG" echo " * PWDBASED: $ENABLED_PWDBASED" echo " * wolfCrypt Only: $ENABLED_CRYPTONLY" echo " * HKDF: $ENABLED_HKDF" +echo " * X9.63 KDF: $ENABLED_X963KDF" echo " * MD4: $ENABLED_MD4" echo " * PSK: $ENABLED_PSK" echo " * Poly1305: $ENABLED_POLY1305" diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 142f94e32..ecdc408a2 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -96,6 +96,10 @@ ECC Curve Sizes: #include #endif +#ifdef HAVE_X963_KDF + #include +#endif + #ifdef NO_INLINE #include #else @@ -6976,6 +6980,130 @@ int wc_ecc_set_custom_curve(ecc_key* key, const ecc_set_type* dp) } #endif /* WOLFSSL_CUSTOM_CURVES */ +#ifdef HAVE_X963_KDF + +#ifndef WOLFSSL_HAVE_MIN +#define WOLFSSL_HAVE_MIN + + static INLINE word32 min(word32 a, word32 b) + { + return a > b ? b : a; + } + +#endif /* WOLFSSL_HAVE_MIN */ + +static INLINE void IncrementX963KdfCounter(byte* inOutCtr) +{ + int i; + + /* in network byte order so start at end and work back */ + for (i = 3; i >= 0; i--) { + if (++inOutCtr[i]) /* we're done unless we overflow */ + return; + } +} + +/* ASN X9.63 Key Derivation Function (SEC1) */ +int wc_X963_KDF(enum wc_HashType type, const byte* secret, word32 secretSz, + const byte* sinfo, word32 sinfoSz, byte* out, word32 outSz) +{ + int ret, i; + int digestSz, copySz; + int remaining = outSz; + byte* outIdx; + byte counter[4]; + byte tmp[WC_MAX_DIGEST_SIZE]; + +#ifdef WOLFSSL_SMALL_STACK + wc_HashAlg* hash; +#else + wc_HashAlg hash[1]; +#endif + + if (secret == NULL || secretSz == 0 || out == NULL) + return BAD_FUNC_ARG; + + /* X9.63 allowed algos only */ + if (type != WC_HASH_TYPE_SHA && type != WC_HASH_TYPE_SHA224 && + type != WC_HASH_TYPE_SHA256 && type != WC_HASH_TYPE_SHA384 && + type != WC_HASH_TYPE_SHA512) + return BAD_FUNC_ARG; + + digestSz = wc_HashGetDigestSize(type); + if (digestSz < 0) + return digestSz; + +#ifdef WOLFSSL_SMALL_STACK + hash = (wc_HashAlg*)XMALLOC(sizeof(wc_HashAlg), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (hash == NULL) + return MEMORY_E; +#endif + + ret = wc_HashInit(hash, type); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + + outIdx = out; + XMEMSET(counter, 0, sizeof(counter)); + + for (i = 1; remaining > 0; i++) { + + IncrementX963KdfCounter(counter); + + ret = wc_HashUpdate(hash, type, secret, secretSz); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + + ret = wc_HashUpdate(hash, type, counter, sizeof(counter)); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + + if (sinfo) { + ret = wc_HashUpdate(hash, type, sinfo, sinfoSz); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + } + + ret = wc_HashFinal(hash, type, tmp); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + + copySz = min(remaining, digestSz); + XMEMCPY(outIdx, tmp, copySz); + + remaining -= copySz; + outIdx += copySz; + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(hash, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return 0; +} +#endif /* HAVE_X963_KDF */ + #ifdef WOLFSSL_ASYNC_CRYPT diff --git a/wolfcrypt/src/hash.c b/wolfcrypt/src/hash.c index 62d9468a6..87d4d0fe1 100644 --- a/wolfcrypt/src/hash.c +++ b/wolfcrypt/src/hash.c @@ -225,6 +225,190 @@ int wc_Hash(enum wc_HashType hash_type, const byte* data, return ret; } +int wc_HashInit(wc_HashAlg* hash, enum wc_HashType type) +{ + int ret = HASH_TYPE_E; /* Default to hash type error */ + + if (hash == NULL) + return BAD_FUNC_ARG; + + switch (type) { + case WC_HASH_TYPE_MD5: +#ifndef NO_MD5 + wc_InitMd5(&hash->md5); +#endif + break; + case WC_HASH_TYPE_SHA: +#ifndef NO_SHA + ret = wc_InitSha(&hash->sha); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA224: +#ifdef WOLFSSL_SHA224 + ret = wc_InitSha224(&hash->sha224); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA256: +#ifndef NO_SHA256 + ret = wc_InitSha256(&hash->sha256); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA384: +#ifdef WOLFSSL_SHA384 + ret = wc_InitSha384(&hash->sha384); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA512: +#ifdef WOLFSSL_SHA512 + ret = wc_InitSha512(&hash->sha512); + if (ret != 0) + return ret; +#endif + break; + + /* not supported */ + case WC_HASH_TYPE_MD5_SHA: + case WC_HASH_TYPE_MD2: + case WC_HASH_TYPE_MD4: + case WC_HASH_TYPE_NONE: + default: + return BAD_FUNC_ARG; + }; + + return 0; +} + +int wc_HashUpdate(wc_HashAlg* hash, enum wc_HashType type, const byte* data, + word32 dataSz) +{ + int ret = HASH_TYPE_E; /* Default to hash type error */ + + if (hash == NULL || data == NULL) + return BAD_FUNC_ARG; + + switch (type) { + case WC_HASH_TYPE_MD5: +#ifndef NO_MD5 + wc_Md5Update(&hash->md5, data, dataSz); +#endif + break; + case WC_HASH_TYPE_SHA: +#ifndef NO_SHA + ret = wc_ShaUpdate(&hash->sha, data, dataSz); + if (ret != 0) +#endif + return ret; + break; + case WC_HASH_TYPE_SHA224: +#ifdef WOLFSSL_SHA224 + ret = wc_Sha224Update(&hash->sha224, data, dataSz); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA256: +#ifndef NO_SHA256 + ret = wc_Sha256Update(&hash->sha256, data, dataSz); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA384: +#ifdef WOLFSSL_SHA384 + ret = wc_Sha384Update(&hash->sha384, data, dataSz); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA512: +#ifdef WOLFSSL_SHA512 + ret = wc_Sha512Update(&hash->sha512, data, dataSz); + if (ret != 0) + return ret; +#endif + break; + + /* not supported */ + case WC_HASH_TYPE_MD5_SHA: + case WC_HASH_TYPE_MD2: + case WC_HASH_TYPE_MD4: + case WC_HASH_TYPE_NONE: + default: + return BAD_FUNC_ARG; + }; + + return 0; +} + +int wc_HashFinal(wc_HashAlg* hash, enum wc_HashType type, byte* out) +{ + int ret = HASH_TYPE_E; /* Default to hash type error */ + + if (hash == NULL || out == NULL) + return BAD_FUNC_ARG; + + switch (type) { + case WC_HASH_TYPE_MD5: +#ifndef NO_MD5 + wc_Md5Final(&hash->md5, out); +#endif + break; + case WC_HASH_TYPE_SHA: +#ifndef NO_SHA + ret = wc_ShaFinal(&hash->sha, out); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA224: +#ifdef WOLFSSL_SHA224 + ret = wc_Sha224Final(&hash->sha224, out); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA256: +#ifndef NO_SHA256 + ret = wc_Sha256Final(&hash->sha256, out); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA384: +#ifdef WOLFSSL_SHA384 + ret = wc_Sha384Final(&hash->sha384, out); + if (ret != 0) + return ret; +#endif + break; + case WC_HASH_TYPE_SHA512: +#ifdef WOLFSSL_SHA512 + ret = wc_Sha512Final(&hash->sha512, out); + if (ret != 0) + return ret; +#endif + break; + + /* not supported */ + case WC_HASH_TYPE_MD5_SHA: + case WC_HASH_TYPE_MD2: + case WC_HASH_TYPE_MD4: + case WC_HASH_TYPE_NONE: + default: + return BAD_FUNC_ARG; + }; + + return 0; +} + #if !defined(WOLFSSL_TI_HASH) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index ef7e4756e..fc0fa086e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -185,6 +185,7 @@ int hmac_sha384_test(void); int hmac_sha512_test(void); int hmac_blake2b_test(void); int hkdf_test(void); +int x963kdf_test(void); int arc4_test(void); int hc128_test(void); int rabbit_test(void); @@ -475,6 +476,13 @@ int wolfcrypt_test(void* args) #endif +#ifdef HAVE_X963_KDF + if ( (ret = x963kdf_test()) != 0) + return err_sys("X963-KDF test failed!\n", ret); + else + printf( "X963-KDF test passed!\n"); +#endif + #ifdef HAVE_AESGCM if ( (ret = gmac_test()) != 0) return err_sys("GMAC test failed!\n", ret); @@ -7108,6 +7116,152 @@ int hkdf_test(void) #endif /* HAVE_HKDF */ +#if defined(HAVE_X963_KDF) + +int x963kdf_test(void) +{ + int ret; + byte kek[128]; + +#ifndef NO_SHA + /* SHA-1, COUNT = 0 + * shared secret length: 192 + * SharedInfo length: 0 + * key data length: 128 + */ + const byte Z[] = { + 0x1c, 0x7d, 0x7b, 0x5f, 0x05, 0x97, 0xb0, 0x3d, + 0x06, 0xa0, 0x18, 0x46, 0x6e, 0xd1, 0xa9, 0x3e, + 0x30, 0xed, 0x4b, 0x04, 0xdc, 0x64, 0xcc, 0xdd + }; + + const byte verify[] = { + 0xbf, 0x71, 0xdf, 0xfd, 0x8f, 0x4d, 0x99, 0x22, + 0x39, 0x36, 0xbe, 0xb4, 0x6f, 0xee, 0x8c, 0xcc + }; +#endif + +#ifndef NO_SHA256 + /* SHA-256, COUNT = 3 + * shared secret length: 192 + * SharedInfo length: 0 + * key data length: 128 + */ + const byte Z2[] = { + 0xd3, 0x8b, 0xdb, 0xe5, 0xc4, 0xfc, 0x16, 0x4c, + 0xdd, 0x96, 0x7f, 0x63, 0xc0, 0x4f, 0xe0, 0x7b, + 0x60, 0xcd, 0xe8, 0x81, 0xc2, 0x46, 0x43, 0x8c + }; + + const byte verify2[] = { + 0x5e, 0x67, 0x4d, 0xb9, 0x71, 0xba, 0xc2, 0x0a, + 0x80, 0xba, 0xd0, 0xd4, 0x51, 0x4d, 0xc4, 0x84 + }; +#endif + +#ifdef WOLFSSL_SHA512 + /* SHA-512, COUNT = 0 + * shared secret length: 192 + * SharedInfo length: 0 + * key data length: 128 + */ + const byte Z3[] = { + 0x87, 0xfc, 0x0d, 0x8c, 0x44, 0x77, 0x48, 0x5b, + 0xb5, 0x74, 0xf5, 0xfc, 0xea, 0x26, 0x4b, 0x30, + 0x88, 0x5d, 0xc8, 0xd9, 0x0a, 0xd8, 0x27, 0x82 + }; + + const byte verify3[] = { + 0x94, 0x76, 0x65, 0xfb, 0xb9, 0x15, 0x21, 0x53, + 0xef, 0x46, 0x02, 0x38, 0x50, 0x6a, 0x02, 0x45 + }; + + /* SHA-512, COUNT = 0 + * shared secret length: 521 + * SharedInfo length: 128 + * key data length: 1024 + */ + const byte Z4[] = { + 0x00, 0xaa, 0x5b, 0xb7, 0x9b, 0x33, 0xe3, 0x89, + 0xfa, 0x58, 0xce, 0xad, 0xc0, 0x47, 0x19, 0x7f, + 0x14, 0xe7, 0x37, 0x12, 0xf4, 0x52, 0xca, 0xa9, + 0xfc, 0x4c, 0x9a, 0xdb, 0x36, 0x93, 0x48, 0xb8, + 0x15, 0x07, 0x39, 0x2f, 0x1a, 0x86, 0xdd, 0xfd, + 0xb7, 0xc4, 0xff, 0x82, 0x31, 0xc4, 0xbd, 0x0f, + 0x44, 0xe4, 0x4a, 0x1b, 0x55, 0xb1, 0x40, 0x47, + 0x47, 0xa9, 0xe2, 0xe7, 0x53, 0xf5, 0x5e, 0xf0, + 0x5a, 0x2d + }; + + const byte info4[] = { + 0xe3, 0xb5, 0xb4, 0xc1, 0xb0, 0xd5, 0xcf, 0x1d, + 0x2b, 0x3a, 0x2f, 0x99, 0x37, 0x89, 0x5d, 0x31 + }; + + const byte verify4[] = { + 0x44, 0x63, 0xf8, 0x69, 0xf3, 0xcc, 0x18, 0x76, + 0x9b, 0x52, 0x26, 0x4b, 0x01, 0x12, 0xb5, 0x85, + 0x8f, 0x7a, 0xd3, 0x2a, 0x5a, 0x2d, 0x96, 0xd8, + 0xcf, 0xfa, 0xbf, 0x7f, 0xa7, 0x33, 0x63, 0x3d, + 0x6e, 0x4d, 0xd2, 0xa5, 0x99, 0xac, 0xce, 0xb3, + 0xea, 0x54, 0xa6, 0x21, 0x7c, 0xe0, 0xb5, 0x0e, + 0xef, 0x4f, 0x6b, 0x40, 0xa5, 0xc3, 0x02, 0x50, + 0xa5, 0xa8, 0xee, 0xee, 0x20, 0x80, 0x02, 0x26, + 0x70, 0x89, 0xdb, 0xf3, 0x51, 0xf3, 0xf5, 0x02, + 0x2a, 0xa9, 0x63, 0x8b, 0xf1, 0xee, 0x41, 0x9d, + 0xea, 0x9c, 0x4f, 0xf7, 0x45, 0xa2, 0x5a, 0xc2, + 0x7b, 0xda, 0x33, 0xca, 0x08, 0xbd, 0x56, 0xdd, + 0x1a, 0x59, 0xb4, 0x10, 0x6c, 0xf2, 0xdb, 0xbc, + 0x0a, 0xb2, 0xaa, 0x8e, 0x2e, 0xfa, 0x7b, 0x17, + 0x90, 0x2d, 0x34, 0x27, 0x69, 0x51, 0xce, 0xcc, + 0xab, 0x87, 0xf9, 0x66, 0x1c, 0x3e, 0x88, 0x16 + }; +#endif + +#ifndef NO_SHA + ret = wc_X963_KDF(WC_HASH_TYPE_SHA, Z, sizeof(Z), NULL, 0, + kek, sizeof(verify)); + if (ret != 0) + return -2001; + + if (XMEMCMP(verify, kek, sizeof(verify)) != 0) + return -2002; +#endif + +#ifndef NO_SHA256 + ret = wc_X963_KDF(WC_HASH_TYPE_SHA256, Z2, sizeof(Z2), NULL, 0, + kek, sizeof(verify2)); + if (ret != 0) + return -2003; + + if (XMEMCMP(verify2, kek, sizeof(verify2)) != 0) + return -2004; +#endif + +#ifdef WOLFSSL_SHA512 + ret = wc_X963_KDF(WC_HASH_TYPE_SHA512, Z3, sizeof(Z3), NULL, 0, + kek, sizeof(verify3)); + if (ret != 0) + return -2005; + + if (XMEMCMP(verify3, kek, sizeof(verify3)) != 0) + return -2006; + + ret = wc_X963_KDF(WC_HASH_TYPE_SHA512, Z4, sizeof(Z4), info4, + sizeof(info4), kek, sizeof(verify4)); + if (ret != 0) + return -2007; + + if (XMEMCMP(verify4, kek, sizeof(verify4)) != 0) + return -2008; +#endif + + return 0; +} + +#endif /* HAVE_X963_KDF */ + + #ifdef HAVE_ECC #ifndef NO_ECC_VECTOR_TEST diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index 2ae7d2a45..060e5f861 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -30,6 +30,10 @@ #include #include +#ifdef HAVE_X963_KDF + #include +#endif + #ifdef WOLFSSL_ASYNC_CRYPT #include #endif @@ -453,6 +457,12 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, #endif /* HAVE_ECC_ENCRYPT */ +#ifdef HAVE_X963_KDF +WOLFSSL_API int wc_X963_KDF(enum wc_HashType type, const byte* secret, + word32 secretSz, const byte* sinfo, word32 sinfoSz, + byte* out, word32 outSz); +#endif + #ifdef WOLFSSL_ASYNC_CRYPT WOLFSSL_API int wc_ecc_async_handle(ecc_key* key, WOLF_EVENT_QUEUE* queue, WOLF_EVENT* event); diff --git a/wolfssl/wolfcrypt/hash.h b/wolfssl/wolfcrypt/hash.h index 4c06ea7ea..fa1883bc6 100644 --- a/wolfssl/wolfcrypt/hash.h +++ b/wolfssl/wolfcrypt/hash.h @@ -25,6 +25,19 @@ #include +#ifndef NO_MD5 + #include +#endif +#ifndef NO_SHA + #include +#endif +#if defined(WOLFSSL_SHA224) || !defined(NO_SHA256) + #include +#endif +#if defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512) + #include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -43,6 +56,27 @@ enum wc_HashType { WC_HASH_TYPE_MD5_SHA = 8, }; +typedef union { + #ifndef NO_MD5 + Md5 md5; + #endif + #ifndef NO_SHA + Sha sha; + #endif + #ifdef WOLFSSL_SHA224 + Sha224 sha224; + #endif + #ifndef NO_SHA256 + Sha256 sha256; + #endif + #ifdef WOLFSSL_SHA384 + Sha384 sha384; + #endif + #ifdef WOLFSSL_SHA512 + Sha512 sha512; + #endif +} wc_HashAlg; + /* Find largest possible digest size Note if this gets up to the size of 80 or over check smallstack build */ #if defined(WOLFSSL_SHA512) @@ -70,6 +104,13 @@ WOLFSSL_API int wc_Hash(enum wc_HashType hash_type, const byte* data, word32 data_len, byte* hash, word32 hash_len); +/* generic hash operation wrappers */ +WOLFSSL_API int wc_HashInit(wc_HashAlg* hash, enum wc_HashType type); +WOLFSSL_API int wc_HashUpdate(wc_HashAlg* hash, enum wc_HashType type, + const byte* data, word32 dataSz); +WOLFSSL_API int wc_HashFinal(wc_HashAlg* hash, enum wc_HashType type, + byte* out); + #ifndef NO_MD5 #include