diff --git a/crypto/kdf/Makefile b/crypto/kdf/Makefile new file mode 100644 index 00000000..8dda5435 --- /dev/null +++ b/crypto/kdf/Makefile @@ -0,0 +1,26 @@ +CC=gcc +WOLFSSL_INSTALL_DIR=/usr/local +CFLAGS=-Wall -I$(WOLFSSL_INSTALL_DIR)/include +LIBS=-L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl -lm + +all: hkdf pbkdf2 scrypt-kdf + +hkdf: hkdf.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +pbkdf2: pbkdf2.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +scrypt-kdf: scrypt-kdf.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean all check + +clean: + rm -f *.o hkdf pbkdf2 scrypt-kdf + +check: all + out=$$(./hkdf) && printf '%s' "$$out" | grep -q 'matches RFC 5869 Test Case 1' + out=$$(./pbkdf2) && printf '%s' "$$out" | grep -q 'matches RFC 7914 test vector' + out=$$(./scrypt-kdf) && printf '%s' "$$out" | grep -q 'matches RFC 7914 test vector' + @echo "PASS: crypto-kdf checks" diff --git a/crypto/kdf/README.md b/crypto/kdf/README.md new file mode 100644 index 00000000..b01a9fa3 --- /dev/null +++ b/crypto/kdf/README.md @@ -0,0 +1,34 @@ +# wolfSSL KDF Examples + +Demonstrates the main wolfCrypt key derivation functions, each verified +against its RFC known-answer test vector. + +* `hkdf.c` - HKDF (RFC 5869): extract-then-expand derivation from existing + keying material, shown both as separate `wc_HKDF_Extract()` / + `wc_HKDF_Expand()` steps and as the one-shot `wc_HKDF()`. +* `pbkdf2.c` - PBKDF2 (RFC 2898) via `wc_PBKDF2()`: deriving keys from + passwords with a salt and an iteration work factor. +* `scrypt-kdf.c` - scrypt (RFC 7914) via `wc_scrypt()`: memory-hard + password-based derivation for stronger resistance to GPU/ASIC attacks. + +Use HKDF when the input is already a high-entropy secret (e.g. a DH shared +secret); use PBKDF2 or scrypt when the input is a password. + +## Building wolfSSL + +``` +./configure --enable-hkdf --enable-scrypt +make +sudo make install +``` + +PBKDF2 is enabled by default (disabled only by `NO_PWDBASED`). + +## Building and running the examples + +``` +make +./hkdf +./pbkdf2 +./scrypt-kdf +``` diff --git a/crypto/kdf/hkdf.c b/crypto/kdf/hkdf.c new file mode 100644 index 00000000..d5a6b1af --- /dev/null +++ b/crypto/kdf/hkdf.c @@ -0,0 +1,130 @@ +/* hkdf.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * 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-1301, USA + */ + +/* Example of HKDF (RFC 5869): extract-then-expand key derivation, run against + * RFC 5869 Test Case 1. */ + +#include +#include + +#include +#include +#include + +#ifdef HAVE_HKDF + +/* RFC 5869 Test Case 1 (SHA-256). */ +static const byte ikm[22] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b +}; +static const byte salt[13] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c +}; +static const byte info[10] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 +}; +static const byte expected_prk[32] = { + 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 +}; +static const byte expected_okm[42] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 +}; + +static void print_hex(const char* label, const byte* data, word32 len) +{ + word32 i; + + printf("%s: ", label); + for (i = 0; i < len; i++) + printf("%02x", data[i]); + printf("\n"); +} + +int main(void) +{ + int ret; + byte prk[32]; + byte okm[42]; + + /* Extract: concentrate the input keying material into a fixed-size PRK. */ + ret = wc_HKDF_Extract(WC_SHA256, salt, sizeof(salt), ikm, sizeof(ikm), + prk); + if (ret != 0) { + printf("wc_HKDF_Extract failed %d\n", ret); + return 1; + } + print_hex("PRK", prk, sizeof(prk)); + if (memcmp(prk, expected_prk, sizeof(prk)) != 0) { + printf("PRK does not match RFC 5869 test vector!\n"); + return 1; + } + + /* Expand: stretch the PRK into the output keying material. */ + ret = wc_HKDF_Expand(WC_SHA256, prk, sizeof(prk), info, sizeof(info), + okm, sizeof(okm)); + if (ret != 0) { + printf("wc_HKDF_Expand failed %d\n", ret); + return 1; + } + print_hex("OKM", okm, sizeof(okm)); + if (memcmp(okm, expected_okm, sizeof(okm)) != 0) { + printf("OKM does not match RFC 5869 test vector!\n"); + return 1; + } + + /* wc_HKDF does both steps in one call. */ + memset(okm, 0, sizeof(okm)); + ret = wc_HKDF(WC_SHA256, ikm, sizeof(ikm), salt, sizeof(salt), info, + sizeof(info), okm, sizeof(okm)); + if (ret != 0) { + printf("wc_HKDF failed %d\n", ret); + return 1; + } + if (memcmp(okm, expected_okm, sizeof(okm)) != 0) { + printf("One-shot OKM does not match!\n"); + return 1; + } + printf("HKDF output matches RFC 5869 Test Case 1\n"); + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-hkdf\n"); + return 0; +} + +#endif /* HAVE_HKDF */ diff --git a/crypto/kdf/pbkdf2.c b/crypto/kdf/pbkdf2.c new file mode 100644 index 00000000..1e5f6052 --- /dev/null +++ b/crypto/kdf/pbkdf2.c @@ -0,0 +1,117 @@ +/* pbkdf2.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * 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-1301, USA + */ + +/* Example of PBKDF2 (RFC 2898) password-based key derivation, run against the + * PBKDF2-HMAC-SHA256 test vector from RFC 7914 Section 11, then with + * realistic parameters. */ + +#include +#include + +#include +#include +#include +#include + +#ifndef NO_PWDBASED + +/* RFC 7914 Section 11: PBKDF2-HMAC-SHA256, P="passwd", S="salt", c=1, + * dkLen=64. */ +static const byte expected_dk[64] = { + 0x55, 0xac, 0x04, 0x6e, 0x56, 0xe3, 0x08, 0x9f, + 0xec, 0x16, 0x91, 0xc2, 0x25, 0x44, 0xb6, 0x05, + 0xf9, 0x41, 0x85, 0x21, 0x6d, 0xde, 0x04, 0x65, + 0xe6, 0x8b, 0x9d, 0x57, 0xc2, 0x0d, 0xac, 0xbc, + 0x49, 0xca, 0x9c, 0xcc, 0xf1, 0x79, 0xb6, 0x45, + 0x99, 0x16, 0x64, 0xb3, 0x9d, 0x77, 0xef, 0x31, + 0x7c, 0x71, 0xb8, 0x45, 0xb1, 0xe3, 0x0b, 0xd5, + 0x09, 0x11, 0x20, 0x41, 0xd3, 0xa1, 0x97, 0x83 +}; + +static void print_hex(const char* label, const byte* data, word32 len) +{ + word32 i; + + printf("%s: ", label); + for (i = 0; i < len; i++) + printf("%02x", data[i]); + printf("\n"); +} + +int main(void) +{ + int ret; + byte dk[64]; + WC_RNG rng; + byte salt[16]; + const char* password = "correct horse battery staple"; + + /* Known-answer check. */ + ret = wc_PBKDF2(dk, (const byte*)"passwd", 6, (const byte*)"salt", 4, 1, + (int)sizeof(dk), WC_SHA256); + if (ret != 0) { + printf("wc_PBKDF2 failed %d\n", ret); + return 1; + } + if (memcmp(dk, expected_dk, sizeof(dk)) != 0) { + printf("Derived key does not match RFC 7914 test vector!\n"); + return 1; + } + printf("Derived key matches RFC 7914 test vector\n"); + + /* Realistic use: random per-user salt and a high iteration count. The + * iteration count is the work factor; NIST SP 800-132 requires at least + * 1000, modern guidance is 600000+ for SHA-256. */ + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + return 1; + } + ret = wc_RNG_GenerateBlock(&rng, salt, sizeof(salt)); + wc_FreeRng(&rng); + if (ret != 0) { + printf("wc_RNG_GenerateBlock failed %d\n", ret); + return 1; + } + + ret = wc_PBKDF2(dk, (const byte*)password, (int)strlen(password), salt, + (int)sizeof(salt), 600000, 32, WC_SHA256); + if (ret != 0) { + printf("wc_PBKDF2 failed %d\n", ret); + return 1; + } + print_hex("salt", salt, sizeof(salt)); + print_hex("key ", dk, 32); + printf("Derived 32-byte key with 600000 iterations\n"); + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL without NO_PWDBASED (PBKDF2 is on by " + "default)\n"); + return 0; +} + +#endif /* !NO_PWDBASED */ diff --git a/crypto/kdf/scrypt-kdf.c b/crypto/kdf/scrypt-kdf.c new file mode 100644 index 00000000..5c8a851f --- /dev/null +++ b/crypto/kdf/scrypt-kdf.c @@ -0,0 +1,89 @@ +/* scrypt-kdf.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * 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-1301, USA + */ + +/* Example of scrypt (RFC 7914) memory-hard password-based key derivation, + * run against the test vector from RFC 7914 Section 12. */ + +#include +#include + +#include +#include +#include + +#ifdef HAVE_SCRYPT + +/* RFC 7914 Section 12, vector 2: P="password", S="NaCl", N=1024, r=8, p=16, + * dkLen=64. */ +static const byte expected_dk[64] = { + 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, + 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe, + 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, + 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62, + 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, + 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda, + 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d, + 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40 +}; + +static void print_hex(const char* label, const byte* data, word32 len) +{ + word32 i; + + printf("%s: ", label); + for (i = 0; i < len; i++) + printf("%02x", data[i]); + printf("\n"); +} + +int main(void) +{ + int ret; + byte dk[64]; + + /* cost is log2(N): 10 -> N=1024. r (block size) scales memory use, + * p (parallelization) scales CPU cost. */ + ret = wc_scrypt(dk, (const byte*)"password", 8, (const byte*)"NaCl", 4, + 10, 8, 16, (int)sizeof(dk)); + if (ret != 0) { + printf("wc_scrypt failed %d\n", ret); + return 1; + } + print_hex("key", dk, sizeof(dk)); + + if (memcmp(dk, expected_dk, sizeof(dk)) != 0) { + printf("Derived key does not match RFC 7914 test vector!\n"); + return 1; + } + printf("Derived key matches RFC 7914 test vector\n"); + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-scrypt\n"); + return 0; +} + +#endif /* HAVE_SCRYPT */