diff --git a/.github/examples-manifest.yml b/.github/examples-manifest.yml index 8fdef6150..7991793f5 100644 --- a/.github/examples-manifest.yml +++ b/.github/examples-manifest.yml @@ -54,13 +54,16 @@ profiles: flags: "--enable-opensslall --enable-opensslextra --enable-static --enable-shared" crypto: - # union of crypto/*: 3des, aes, aes-modes, camellia, ascon, keywrap, pkcs12 + # union of crypto/* and hash/*: 3des, aes, aes-modes, camellia, ascon, + # keywrap, kdf, pkcs12, siphash, blake2 flags: >- --enable-pwdbased --enable-des3 --enable-camellia --enable-ascon --enable-experimental --enable-aesgcm-stream --enable-aesccm --enable-aesctr --enable-aescfb --enable-aesofb --enable-aeseax --enable-aessiv --enable-aesxts --enable-aeskeywrap --enable-keygen --enable-certgen - --enable-certext --enable-pkcs12 --enable-static --enable-shared + --enable-certext --enable-pkcs12 --enable-blake2 --enable-blake2s + --enable-siphash --enable-hkdf --enable-scrypt + --enable-static --enable-shared # aes-cts and aes-ecb have no configure flag: without these defines both # compile to a stub main() that prints "not compiled in" and returns 0. # WC_RNG_SEED_CB likewise has no --enable of its own (only opensslextra and @@ -87,7 +90,7 @@ profiles: flags: >- --enable-ecc --enable-ed25519 --enable-ed448 --enable-curve25519 --enable-curve448 --enable-keygen --enable-rsapss --enable-srp --enable-hpke - --enable-aesgcm --enable-static --enable-shared + --enable-aesgcm --enable-eccsi --enable-sakke --enable-static --enable-shared # WOLFSSL_RSA_KEY_CHECK has no configure option: pk/rsa-kg calls # wc_CheckRsaKey, which rsa.c only defines under that macro. cflags: "-DWOLFSSL_PUBLIC_MP -DUSE_CERT_BUFFERS_2048 -DWOLFSSL_ECDSA_DETERMINISTIC_K -DWOLFSSL_RSA_KEY_CHECK" @@ -146,7 +149,7 @@ profiles: # so without it the client dies on "failed to set the requested group". flags: >- --enable-mlkem --enable-dilithium --enable-lms --enable-xmss - --enable-extra-pqc-hybrids + --enable-extra-pqc-hybrids --enable-slhdsa=yes,sha2 --enable-experimental --enable-tls13 --enable-static --enable-shared acert: @@ -345,6 +348,11 @@ examples: # openssl dgst, so these assert the algorithm is right, not just that it ran. # input.txt is tracked -- if it changes on purpose, recompute these. + - id: hash-blake2 + path: hash/blake2 + profile: crypto + mode: check + - id: embedded path: embedded profile: default @@ -405,6 +413,11 @@ examples: profile: crypto mode: check + - id: crypto-kdf + path: crypto/kdf + profile: crypto + mode: check + - id: crypto-keywrap path: crypto/keywrap profile: crypto @@ -415,6 +428,19 @@ examples: profile: crypto mode: check + - id: crypto-siphash + path: crypto/siphash + profile: crypto + mode: check + + - id: crypto-sm + path: crypto/sm + mode: skip + reason: >- + SM2/SM3/SM4 live in the separate wolfSSL/wolfsm overlay, which must be + installed into the wolfSSL source tree before configure. No cached + profile can express that patch step yet. + - id: signature path: signature profile: default @@ -590,6 +616,11 @@ examples: profile: pq mode: check + - id: pq-slh-dsa + path: pq/slh_dsa + profile: pq + mode: check + - id: pq-stateful-hash-sig path: pq/stateful_hash_sig profile: pq @@ -742,6 +773,10 @@ examples: path: pk/hpke profile: pk mode: check + - id: pk-mikey-sakke + path: pk/mikey-sakke + profile: pk + mode: check - id: pk-rsa-kg path: pk/rsa-kg profile: pk diff --git a/crypto/kdf/Makefile b/crypto/kdf/Makefile new file mode 100644 index 000000000..8dda5435d --- /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 000000000..b01a9fa31 --- /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 000000000..d5a6b1af4 --- /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 000000000..1e5f6052b --- /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 000000000..5c8a851fe --- /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 */ diff --git a/crypto/siphash/Makefile b/crypto/siphash/Makefile new file mode 100644 index 000000000..b1ebba2f9 --- /dev/null +++ b/crypto/siphash/Makefile @@ -0,0 +1,16 @@ +CC=gcc +WOLFSSL_INSTALL_DIR=/usr/local +CFLAGS=-Wall -I$(WOLFSSL_INSTALL_DIR)/include +LIBS=-L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl -lm + +siphash-mac: siphash-mac.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean check + +clean: + rm -f *.o siphash-mac + +check: siphash-mac + out=$$(./siphash-mac) && printf '%s' "$$out" | grep -q 'Incremental MAC matches one-shot MAC' + @echo "PASS: crypto-siphash checks" diff --git a/crypto/siphash/README.md b/crypto/siphash/README.md new file mode 100644 index 000000000..470d99652 --- /dev/null +++ b/crypto/siphash/README.md @@ -0,0 +1,24 @@ +# wolfSSL SipHash Example + +Demonstrates SipHash-2-4 keyed MACs with the `wc_SipHash*` API: one-shot +64-bit and 128-bit tags plus the incremental Init/Update/Final interface, +checked against the reference implementation's test vector. + +SipHash is a fast keyed pseudorandom function for short inputs. Typical uses +are hash-table flooding protection and lightweight per-packet authentication; +it is not a general-purpose collision-resistant hash. + +## Building wolfSSL + +``` +./configure --enable-siphash +make +sudo make install +``` + +## Building and running the example + +``` +make +./siphash-mac +``` diff --git a/crypto/siphash/siphash-mac.c b/crypto/siphash/siphash-mac.c new file mode 100644 index 000000000..ab14f13fb --- /dev/null +++ b/crypto/siphash/siphash-mac.c @@ -0,0 +1,119 @@ +/* siphash-mac.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 SipHash-2-4 as a short-output keyed MAC. + * + * SipHash is designed for short inputs (hash table keys, network packet + * authentication) where a fast 64- or 128-bit keyed MAC is enough. */ + +#include +#include + +#include +#include +#include + +#ifdef WOLFSSL_SIPHASH + +/* Reference test vector from https://github.com/veorq/SipHash: key 00..0f, + * message 00..0e, 8-byte output. */ +static const byte kat_key[SIPHASH_KEY_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f +}; +static const byte kat_msg[15] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e +}; +static const byte kat_mac[SIPHASH_MAC_SIZE_8] = { + 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1 +}; + +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; + SipHash sipHash; + byte mac8[SIPHASH_MAC_SIZE_8]; + byte mac16[SIPHASH_MAC_SIZE_16]; + + /* One-shot with an 8-byte tag, checked against the reference vector. */ + ret = wc_SipHash(kat_key, kat_msg, sizeof(kat_msg), mac8, + SIPHASH_MAC_SIZE_8); + if (ret != 0) { + printf("wc_SipHash failed %d\n", ret); + return 1; + } + print_hex("SipHash-2-4 64-bit ", mac8, sizeof(mac8)); + if (memcmp(mac8, kat_mac, sizeof(kat_mac)) != 0) { + printf("MAC does not match reference test vector!\n"); + return 1; + } + printf("MAC matches reference test vector\n"); + + /* One-shot with a 16-byte tag. */ + ret = wc_SipHash(kat_key, kat_msg, sizeof(kat_msg), mac16, + SIPHASH_MAC_SIZE_16); + if (ret != 0) { + printf("wc_SipHash failed %d\n", ret); + return 1; + } + print_hex("SipHash-2-4 128-bit", mac16, sizeof(mac16)); + + /* Incremental API produces the same tag as one-shot. */ + ret = wc_InitSipHash(&sipHash, kat_key, SIPHASH_MAC_SIZE_8); + if (ret == 0) + ret = wc_SipHashUpdate(&sipHash, kat_msg, 8); + if (ret == 0) + ret = wc_SipHashUpdate(&sipHash, kat_msg + 8, sizeof(kat_msg) - 8); + if (ret == 0) + ret = wc_SipHashFinal(&sipHash, mac8, SIPHASH_MAC_SIZE_8); + if (ret != 0) { + printf("incremental SipHash failed %d\n", ret); + return 1; + } + if (memcmp(mac8, kat_mac, sizeof(kat_mac)) != 0) { + printf("Incremental MAC does not match one-shot MAC!\n"); + return 1; + } + printf("Incremental MAC matches one-shot MAC\n"); + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-siphash\n"); + return 0; +} + +#endif /* WOLFSSL_SIPHASH */ diff --git a/crypto/sm/Makefile b/crypto/sm/Makefile new file mode 100644 index 000000000..0f496d797 --- /dev/null +++ b/crypto/sm/Makefile @@ -0,0 +1,30 @@ +CC=gcc +WOLFSSL_INSTALL_DIR=/usr/local +CFLAGS=-Wall -I$(WOLFSSL_INSTALL_DIR)/include +LIBS=-L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl -lm + +all: sm3-hash sm4-gcm-encrypt sm2-sign-verify sm2-ecdh + +sm3-hash: sm3-hash.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +sm4-gcm-encrypt: sm4-gcm-encrypt.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +sm2-sign-verify: sm2-sign-verify.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +sm2-ecdh: sm2-ecdh.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean all check + +clean: + rm -f *.o sm3-hash sm4-gcm-encrypt sm2-sign-verify sm2-ecdh + +check: all + out=$$(./sm3-hash) && printf '%s' "$$out" | grep -q 'matches GB/T 32905 test vector' + out=$$(./sm4-gcm-encrypt) && printf '%s' "$$out" | grep -q 'Tampered tag rejected as expected' + out=$$(./sm2-sign-verify) && printf '%s' "$$out" | grep -q 'Signature verified' + out=$$(./sm2-ecdh) && printf '%s' "$$out" | grep -q 'Shared secrets match' + @echo "PASS: crypto-sm checks" diff --git a/crypto/sm/README.md b/crypto/sm/README.md new file mode 100644 index 000000000..6a28d0124 --- /dev/null +++ b/crypto/sm/README.md @@ -0,0 +1,43 @@ +# wolfSSL SM2/SM3/SM4 Examples + +Demonstrates the Chinese national (ShangMi) cryptographic algorithms at the +wolfCrypt level: + +* `sm3-hash.c` - SM3 hash (Chinese national standard GB/T 32905-2016), + checked against the standard's "abc" test vector. +* `sm4-gcm-encrypt.c` - SM4-GCM (GB/T 32907-2016) authenticated encryption + with tamper detection. +* `sm2-sign-verify.c` - SM2 (GB/T 32918) digital signatures, including the + identity-based "ZA" digest step via `wc_ecc_sm2_create_digest()`. +* `sm2-ecdh.c` - ECDH shared-secret agreement on the SM2 curve. + +## Building wolfSSL + +The SM algorithm implementations ship in the separate +[wolfSSL/wolfsm](https://github.com/wolfSSL/wolfsm) overlay, so install that +into a wolfSSL source tree first: + +``` +git clone https://github.com/wolfSSL/wolfsm +git clone https://github.com/wolfSSL/wolfssl +cd wolfsm +./install.sh ../wolfssl +cd ../wolfssl +./autogen.sh +./configure --enable-sm2 --enable-sm3 --enable-sm4-gcm +make +sudo make install +``` + +Other SM4 modes are available with `--enable-sm4-ecb`, `--enable-sm4-cbc`, +`--enable-sm4-ctr` and `--enable-sm4-ccm`. + +## Building and running the examples + +``` +make +./sm3-hash [message] +./sm4-gcm-encrypt +./sm2-sign-verify [message] +./sm2-ecdh +``` diff --git a/crypto/sm/sm2-ecdh.c b/crypto/sm/sm2-ecdh.c new file mode 100644 index 000000000..2eaa9a684 --- /dev/null +++ b/crypto/sm/sm2-ecdh.c @@ -0,0 +1,243 @@ +/* sm2-ecdh.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 ECDH over the SM2 curve: both sides compute the same shared + * secret from their private key and the peer's public key. + * + * This is plain ECDH on the SM2P256V1 curve, not the full SM2 key exchange + * protocol from GB/T 32918.3. + * + * Roles in this example: + * Alice: makes an SM2 key pair, sends her public key to Bob, and combines + * her private key with Bob's public key. + * Bob: does the same in the other direction. + * + * Only public keys are exchanged, so each side is given its own ecc_key for + * the peer, imported from the bytes that crossed the wire. Both then arrive + * at the same secret without it ever being transmitted. */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef WOLFSSL_SM2 + +#define SM2_FIELD_SZ 32 /* SM2P256V1: 256-bit curve */ +#define SM2_SECRET_SZ SM2_FIELD_SZ /* ECDH secret is one X ord */ +#define SM2_PUB_KEY_SZ (1 + SM2_FIELD_SZ * 2) /* X9.63 point: 04 || X || Y */ + +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; + + WC_RNG rng; + int rngInit = 0; + + /* Every struct below is declared before the first "goto exit" so that the + * cleanup at the bottom always sees initialised members. */ + struct { + byte publicKey[SM2_PUB_KEY_SZ]; /* only public data is sent */ + word32 publicKeySz; + } Wire = {0}; + + struct { + ecc_key key; /* Alice's own key pair */ + int keyInit; + ecc_key peerKey; /* Alice view: Bob's pubkey */ + int peerKeyInit; + byte sharedSecret[SM2_SECRET_SZ]; + word32 sharedSecretSz; + } Alice = {0}; + + struct { + ecc_key key; /* Bob's own key pair */ + int keyInit; + ecc_key peerKey; /* Bob view: Alice's pubkey */ + int peerKeyInit; + byte sharedSecret[SM2_SECRET_SZ]; + word32 sharedSecretSz; + } Bob = {0}; + + /* --- Init Alice --- */ + ret = wc_ecc_init(&Alice.key); + if (ret != 0) goto exit; else Alice.keyInit = 1; + ret = wc_ecc_init(&Alice.peerKey); + if (ret != 0) goto exit; else Alice.peerKeyInit = 1; + Alice.sharedSecretSz = (word32)sizeof(Alice.sharedSecret); + /* --- Init Alice --- */ + + /* --- Init Bob --- */ + ret = wc_ecc_init(&Bob.key); + if (ret != 0) goto exit; else Bob.keyInit = 1; + ret = wc_ecc_init(&Bob.peerKey); + if (ret != 0) goto exit; else Bob.peerKeyInit = 1; + Bob.sharedSecretSz = (word32)sizeof(Bob.sharedSecret); + /* --- Init Bob --- */ + + /* --- One rng instance for simplicity -- */ + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + /* --- One rng instance for simplicity -- */ + + /* --- Each side makes its own SM2 key pair --- */ + ret = wc_ecc_sm2_make_key(&rng, &Alice.key, WC_ECC_FLAG_NONE); + if (ret == 0) + ret = wc_ecc_sm2_make_key(&rng, &Bob.key, WC_ECC_FLAG_NONE); + if (ret != 0) { + printf("wc_ecc_sm2_make_key failed %d\n", ret); + goto exit; + } + printf("Generated two SM2 keys\n"); + /* --- Each side makes its own SM2 key pair --- */ + +#ifdef ECC_TIMING_RESISTANT + /* --- Timing-resistant point math needs an RNG on the private key --- */ + ret = wc_ecc_set_rng(&Alice.key, &rng); + if (ret == 0) + ret = wc_ecc_set_rng(&Bob.key, &rng); + if (ret != 0) { + printf("wc_ecc_set_rng failed %d\n", ret); + goto exit; + } + /* --- Timing-resistant point math needs an RNG on the private key --- */ +#endif + + /* --- Alice sends her public key to Bob --- */ + { + /* - Export Alice's public point (simulate sending pubkey only) - */ + Wire.publicKeySz = SM2_PUB_KEY_SZ; + ret = wc_ecc_export_x963_ex(&Alice.key, Wire.publicKey, + &Wire.publicKeySz, 0); + if (ret != 0) {printf("Could not export Alice's pubkey\n"); goto exit;} + /* - Export Alice's public point (simulate sending pubkey only) - */ + + /* - Bob saves the public key he received - */ + ret = wc_ecc_import_x963_ex(Wire.publicKey, Wire.publicKeySz, + &Bob.peerKey, ECC_SM2P256V1); + if (ret != 0) { + printf("Bob could not import Alice's pubkey\n"); + goto exit; + } + /* - Bob saves the public key he received - */ + } + /* --- Alice sends her public key to Bob --- */ + + /* --- Reset the wire for Bob's public key --- */ + memset(&Wire, 0, sizeof(Wire)); + /* --- Reset the wire for Bob's public key --- */ + + /* --- Bob sends his public key to Alice --- */ + { + /* - Export Bob's public point (simulate sending pubkey only) - */ + Wire.publicKeySz = SM2_PUB_KEY_SZ; + ret = wc_ecc_export_x963_ex(&Bob.key, Wire.publicKey, + &Wire.publicKeySz, 0); + if (ret != 0) {printf("Could not export Bob's pubkey\n"); goto exit;} + /* - Export Bob's public point (simulate sending pubkey only) - */ + + /* - Alice saves the public key she received - */ + ret = wc_ecc_import_x963_ex(Wire.publicKey, Wire.publicKeySz, + &Alice.peerKey, ECC_SM2P256V1); + if (ret != 0) { + printf("Alice could not import Bob's pubkey\n"); + goto exit; + } + /* - Alice saves the public key she received - */ + } + /* --- Bob sends his public key to Alice --- */ + + /* --- Each side computes the shared secret --- */ + /* Own private key plus the peer's public key. The size in is the buffer + * size available; on return it holds the secret length. */ + ret = wc_ecc_sm2_shared_secret(&Alice.key, &Alice.peerKey, + Alice.sharedSecret, &Alice.sharedSecretSz); + if (ret == 0) + ret = wc_ecc_sm2_shared_secret(&Bob.key, &Bob.peerKey, + Bob.sharedSecret, &Bob.sharedSecretSz); + if (ret != 0) { + printf("wc_ecc_sm2_shared_secret failed %d\n", ret); + goto exit; + } + /* --- Each side computes the shared secret --- */ + + print_hex("alice secret", Alice.sharedSecret, Alice.sharedSecretSz); + print_hex("bob secret", Bob.sharedSecret, Bob.sharedSecretSz); + + if (Alice.sharedSecretSz != Bob.sharedSecretSz || + memcmp(Alice.sharedSecret, Bob.sharedSecret, + Alice.sharedSecretSz) != 0) { + printf("Shared secrets differ!\n"); + ret = -1; + goto exit; + } + printf("Shared secrets match\n"); + ret = 0; + +exit: + if (ret != 0) + printf("error %d: %s\n", ret, wc_GetErrorString(ret)); + + if (Bob.peerKeyInit) + wc_ecc_free(&Bob.peerKey); + if (Bob.keyInit) + wc_ecc_free(&Bob.key); + + if (Alice.peerKeyInit) + wc_ecc_free(&Alice.peerKey); + if (Alice.keyInit) + wc_ecc_free(&Alice.key); + + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please install the wolfsm overlay and build wolfSSL with " + "./configure --enable-sm2\n"); + return 0; +} + +#endif /* WOLFSSL_SM2 */ diff --git a/crypto/sm/sm2-sign-verify.c b/crypto/sm/sm2-sign-verify.c new file mode 100644 index 000000000..d6eef2563 --- /dev/null +++ b/crypto/sm/sm2-sign-verify.c @@ -0,0 +1,146 @@ +/* sm2-sign-verify.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 SM2 (GB/T 32918) signing and verifying over the SM2 curve. + * + * SM2 does not sign the raw message: the message is first combined with the + * signer's identity and public key into an SM3 digest (the "ZA" hash). */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) + +/* Default identity from GM/T 0009-2012, also used for certificates. */ +static const byte sm2_id[] = "1234567812345678"; + +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(int argc, char* argv[]) +{ + int ret; + WC_RNG rng; + int rngInit = 0; + ecc_key key; + int keyInit = 0; + byte digest[WC_SM3_DIGEST_SIZE]; + byte sig[ECC_MAX_SIG_SIZE]; + word32 sigSz = (word32)sizeof(sig); + int verified = 0; + const char* msg = (argc > 1) ? argv[1] : "sm2 sign-verify example"; + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + + ret = wc_ecc_init(&key); + if (ret != 0) { + printf("wc_ecc_init failed %d\n", ret); + goto exit; + } + keyInit = 1; + + ret = wc_ecc_sm2_make_key(&rng, &key, WC_ECC_FLAG_NONE); + if (ret != 0) { + printf("wc_ecc_sm2_make_key failed %d\n", ret); + goto exit; + } + printf("Generated SM2 key\n"); + + /* ZA digest: SM3 over the identity, curve parameters and public key, + * then SM3 over ZA || message. */ + ret = wc_ecc_sm2_create_digest(sm2_id, (word16)(sizeof(sm2_id) - 1), + (const byte*)msg, (int)strlen(msg), + WC_HASH_TYPE_SM3, digest, + (int)sizeof(digest), &key); + if (ret != 0) { + printf("wc_ecc_sm2_create_digest failed %d\n", ret); + goto exit; + } + print_hex("digest", digest, sizeof(digest)); + + ret = wc_ecc_sm2_sign_hash(digest, sizeof(digest), sig, &sigSz, &rng, + &key); + if (ret != 0) { + printf("wc_ecc_sm2_sign_hash failed %d\n", ret); + goto exit; + } + print_hex("signature", sig, sigSz); + + ret = wc_ecc_sm2_verify_hash(sig, sigSz, digest, sizeof(digest), + &verified, &key); + if (ret != 0 || verified != 1) { + printf("wc_ecc_sm2_verify_hash failed: ret %d verified %d\n", ret, + verified); + ret = -1; + goto exit; + } + printf("Signature verified\n"); + + /* A modified digest must not verify. */ + digest[0] ^= 0x01; + ret = wc_ecc_sm2_verify_hash(sig, sigSz, digest, sizeof(digest), + &verified, &key); + if (ret == 0 && verified == 1) { + printf("Corrupted digest verified!\n"); + ret = -1; + goto exit; + } + printf("Corrupted digest rejected as expected\n"); + ret = 0; + +exit: + if (keyInit) + wc_ecc_free(&key); + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please install the wolfsm overlay and build wolfSSL with " + "./configure --enable-sm2 --enable-sm3\n"); + return 0; +} + +#endif /* WOLFSSL_SM2 && WOLFSSL_SM3 */ diff --git a/crypto/sm/sm3-hash.c b/crypto/sm/sm3-hash.c new file mode 100644 index 000000000..9c6dab946 --- /dev/null +++ b/crypto/sm/sm3-hash.c @@ -0,0 +1,97 @@ +/* sm3-hash.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 incremental SM3 hashing (GB/T 32905-2016), checked against the + * standard's "abc" test vector. */ + +#include +#include + +#include +#include +#include + +#ifdef WOLFSSL_SM3 + +/* SM3("abc") from GB/T 32905-2016 Appendix A. */ +static const byte kat_abc[WC_SM3_DIGEST_SIZE] = { + 0x66, 0xc7, 0xf0, 0xf4, 0x62, 0xee, 0xed, 0xd9, + 0xd1, 0xf2, 0xd4, 0x6b, 0xdc, 0x10, 0xe4, 0xe2, + 0x41, 0x67, 0xc4, 0x87, 0x5c, 0xf2, 0xf7, 0xa2, + 0x29, 0x7d, 0xa0, 0x2b, 0x8f, 0x4b, 0xa8, 0xe0 +}; + +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(int argc, char* argv[]) +{ + int ret; + wc_Sm3 sm3; + byte digest[WC_SM3_DIGEST_SIZE]; + const char* msg = (argc > 1) ? argv[1] : "abc"; + + ret = wc_InitSm3(&sm3, NULL, INVALID_DEVID); + if (ret != 0) { + printf("wc_InitSm3 failed %d\n", ret); + return 1; + } + + /* Data may be added in as many update calls as needed. */ + ret = wc_Sm3Update(&sm3, (const byte*)msg, (word32)strlen(msg)); + if (ret == 0) + ret = wc_Sm3Final(&sm3, digest); + wc_Sm3Free(&sm3); + if (ret != 0) { + printf("SM3 hash failed %d\n", ret); + return 1; + } + + print_hex("SM3", digest, WC_SM3_DIGEST_SIZE); + + if (argc <= 1) { + if (memcmp(digest, kat_abc, WC_SM3_DIGEST_SIZE) != 0) { + printf("Digest does not match GB/T 32905 test vector!\n"); + return 1; + } + printf("Digest matches GB/T 32905 test vector\n"); + } + + return 0; +} + +#else + +int main(void) +{ + printf("Please install the wolfsm overlay and build wolfSSL with " + "./configure --enable-sm3\n"); + return 0; +} + +#endif /* WOLFSSL_SM3 */ diff --git a/crypto/sm/sm4-gcm-encrypt.c b/crypto/sm/sm4-gcm-encrypt.c new file mode 100644 index 000000000..860c11dfc --- /dev/null +++ b/crypto/sm/sm4-gcm-encrypt.c @@ -0,0 +1,150 @@ +/* sm4-gcm-encrypt.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 SM4-GCM (GB/T 32907-2016 block cipher in GCM mode) authenticated + * encryption: encrypt, decrypt, and reject a tampered tag. */ + +#include +#include + +#include +#include +#include +#include + +#if defined(WOLFSSL_SM4) && defined(WOLFSSL_SM4_GCM) + +#define NONCE_SZ 12 +#define TAG_SZ 16 + +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; + wc_Sm4 sm4; + int sm4Init = 0; + WC_RNG rng; + int rngInit = 0; + byte key[SM4_KEY_SIZE]; + byte nonce[NONCE_SZ]; + byte tag[TAG_SZ]; + const char* msg = "sm4-gcm example plaintext"; + const char* aad = "example aad"; + byte cipher[64]; + byte plain[64]; + word32 msgSz = (word32)strlen(msg); + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + + ret = wc_Sm4Init(&sm4, NULL, INVALID_DEVID); + if (ret != 0) { + printf("wc_Sm4Init failed %d\n", ret); + goto exit; + } + sm4Init = 1; + + /* Fresh random key; a nonce must never repeat under the same key. */ + ret = wc_RNG_GenerateBlock(&rng, key, sizeof(key)); + if (ret == 0) + ret = wc_RNG_GenerateBlock(&rng, nonce, sizeof(nonce)); + if (ret != 0) { + printf("wc_RNG_GenerateBlock failed %d\n", ret); + goto exit; + } + + ret = wc_Sm4GcmSetKey(&sm4, key, sizeof(key)); + if (ret != 0) { + printf("wc_Sm4GcmSetKey failed %d\n", ret); + goto exit; + } + + ret = wc_Sm4GcmEncrypt(&sm4, cipher, (const byte*)msg, msgSz, nonce, + sizeof(nonce), tag, sizeof(tag), (const byte*)aad, + (word32)strlen(aad)); + if (ret != 0) { + printf("wc_Sm4GcmEncrypt failed %d\n", ret); + goto exit; + } + print_hex("key ", key, sizeof(key)); + print_hex("nonce ", nonce, sizeof(nonce)); + print_hex("ciphertext", cipher, msgSz); + print_hex("tag ", tag, sizeof(tag)); + + ret = wc_Sm4GcmDecrypt(&sm4, plain, cipher, msgSz, nonce, sizeof(nonce), + tag, sizeof(tag), (const byte*)aad, + (word32)strlen(aad)); + if (ret != 0) { + printf("wc_Sm4GcmDecrypt failed %d\n", ret); + goto exit; + } + if (memcmp(plain, msg, msgSz) != 0) { + printf("Decrypted plaintext mismatch!\n"); + ret = -1; + goto exit; + } + printf("Decrypt success\n"); + + /* A tampered tag must fail authentication. */ + tag[0] ^= 0x01; + ret = wc_Sm4GcmDecrypt(&sm4, plain, cipher, msgSz, nonce, sizeof(nonce), + tag, sizeof(tag), (const byte*)aad, + (word32)strlen(aad)); + if (ret == 0) { + printf("Tampered tag accepted!\n"); + ret = -1; + goto exit; + } + printf("Tampered tag rejected as expected\n"); + ret = 0; + +exit: + if (sm4Init) + wc_Sm4Free(&sm4); + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please install the wolfsm overlay and build wolfSSL with " + "./configure --enable-sm4-gcm\n"); + return 0; +} + +#endif /* WOLFSSL_SM4 && WOLFSSL_SM4_GCM */ diff --git a/hash/blake2/Makefile b/hash/blake2/Makefile new file mode 100644 index 000000000..740b8c4d8 --- /dev/null +++ b/hash/blake2/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: blake2b-hash blake2s-hash blake2-keyed-mac + +blake2b-hash: blake2b-hash.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +blake2s-hash: blake2s-hash.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +blake2-keyed-mac: blake2-keyed-mac.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean all check + +clean: + rm -f *.o blake2b-hash blake2s-hash blake2-keyed-mac + +check: all + out=$$(./blake2b-hash) && printf '%s' "$$out" | grep -q 'matches RFC 7693 test vector' + out=$$(./blake2s-hash) && printf '%s' "$$out" | grep -q 'matches RFC 7693 test vector' + out=$$(./blake2-keyed-mac) && printf '%s' "$$out" | grep -q 'Wrong key rejected' + @echo "PASS: hash-blake2 checks" diff --git a/hash/blake2/README.md b/hash/blake2/README.md new file mode 100644 index 000000000..4b2ae5372 --- /dev/null +++ b/hash/blake2/README.md @@ -0,0 +1,34 @@ +# wolfSSL BLAKE2 Examples + +Demonstrates the dedicated BLAKE2b/BLAKE2s wolfCrypt APIs (`wc_Blake2b*` / +`wc_Blake2s*`), including BLAKE2's native keyed mode. + +* `blake2b-hash.c` - incremental BLAKE2b-512 hashing, verified against the + RFC 7693 Appendix A test vector. +* `blake2s-hash.c` - incremental BLAKE2s-256 hashing, verified against the + RFC 7693 Appendix B test vector. +* `blake2-keyed-mac.c` - keyed BLAKE2b as a MAC via + `wc_InitBlake2b_WithKey()`. Unlike SHA-2, BLAKE2 does not need the HMAC + construction to be used as a MAC. + +## Building wolfSSL + +``` +./configure --enable-blake2 --enable-blake2s +make +sudo make install +``` + +`--enable-blake2` enables BLAKE2b, `--enable-blake2s` enables BLAKE2s. + +## Building and running the examples + +``` +make +./blake2b-hash [message] +./blake2s-hash [message] +./blake2-keyed-mac +``` + +With no argument the hash examples digest `"abc"` and compare against the +RFC 7693 known-answer vectors. diff --git a/hash/blake2/blake2-keyed-mac.c b/hash/blake2/blake2-keyed-mac.c new file mode 100644 index 000000000..6f3328113 --- /dev/null +++ b/hash/blake2/blake2-keyed-mac.c @@ -0,0 +1,132 @@ +/* blake2-keyed-mac.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 BLAKE2b in keyed mode, used as a MAC. + * + * BLAKE2's native keyed mode replaces the HMAC construction: the key is mixed + * into the initial state, so a single hash pass produces the tag. */ + +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_BLAKE2B + +/* 32-byte tag is plenty for a MAC; BLAKE2b allows 1-64. */ +#define TAG_SZ 32 +#define KEY_SZ 32 + +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"); +} + +static int mac_message(const byte* key, word32 keySz, const char* msg, + byte* tag, word32 tagSz) +{ + int ret; + Blake2b b2b; + + ret = wc_InitBlake2b_WithKey(&b2b, tagSz, key, keySz); + if (ret == 0) + ret = wc_Blake2bUpdate(&b2b, (const byte*)msg, (word32)strlen(msg)); + if (ret == 0) + ret = wc_Blake2bFinal(&b2b, tag, tagSz); + + return ret; +} + +int main(void) +{ + int ret; + WC_RNG rng; + byte key[KEY_SZ]; + byte tag[TAG_SZ]; + byte check[TAG_SZ]; + const char* msg = "authenticate this message"; + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + return 1; + } + + ret = wc_RNG_GenerateBlock(&rng, key, KEY_SZ); + wc_FreeRng(&rng); + if (ret != 0) { + printf("wc_RNG_GenerateBlock failed %d\n", ret); + return 1; + } + + ret = mac_message(key, KEY_SZ, msg, tag, TAG_SZ); + if (ret != 0) { + printf("MAC generation failed %d\n", ret); + return 1; + } + print_hex("key", key, KEY_SZ); + print_hex("tag", tag, TAG_SZ); + + /* Verifier recomputes the tag with the shared key and compares. */ + ret = mac_message(key, KEY_SZ, msg, check, TAG_SZ); + if (ret != 0) { + printf("MAC verification failed %d\n", ret); + return 1; + } + if (memcmp(tag, check, TAG_SZ) != 0) { + printf("MAC mismatch!\n"); + return 1; + } + printf("MAC verified\n"); + + /* A different key must produce a different tag. */ + key[0] ^= 0x01; + ret = mac_message(key, KEY_SZ, msg, check, TAG_SZ); + if (ret != 0) { + printf("MAC computation failed %d\n", ret); + return 1; + } + if (memcmp(tag, check, TAG_SZ) == 0) { + printf("Tag did not change with key!\n"); + return 1; + } + printf("Wrong key rejected\n"); + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-blake2\n"); + return 0; +} + +#endif /* HAVE_BLAKE2B */ diff --git a/hash/blake2/blake2b-hash.c b/hash/blake2/blake2b-hash.c new file mode 100644 index 000000000..f0bf3ad1e --- /dev/null +++ b/hash/blake2/blake2b-hash.c @@ -0,0 +1,104 @@ +/* blake2b-hash.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 incremental BLAKE2b hashing with the wc_Blake2b API. */ + +#include +#include + +#include +#include +#include + +#ifdef HAVE_BLAKE2B + +#define DIGEST_SZ 64 + +/* BLAKE2b-512("abc") from RFC 7693 Appendix A. */ +static const byte kat_abc[DIGEST_SZ] = { + 0xba, 0x80, 0xa5, 0x3f, 0x98, 0x1c, 0x4d, 0x0d, + 0x6a, 0x27, 0x97, 0xb6, 0x9f, 0x12, 0xf6, 0xe9, + 0x4c, 0x21, 0x2f, 0x14, 0x68, 0x5a, 0xc4, 0xb7, + 0x4b, 0x12, 0xbb, 0x6f, 0xdb, 0xff, 0xa2, 0xd1, + 0x7d, 0x87, 0xc5, 0x39, 0x2a, 0xab, 0x79, 0x2d, + 0xc2, 0x52, 0xd5, 0xde, 0x45, 0x33, 0xcc, 0x95, + 0x18, 0xd3, 0x8a, 0xa8, 0xdb, 0xf1, 0x92, 0x5a, + 0xb9, 0x23, 0x86, 0xed, 0xd4, 0x00, 0x99, 0x23 +}; + +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(int argc, char* argv[]) +{ + int ret; + Blake2b b2b; + byte digest[DIGEST_SZ]; + const char* msg = (argc > 1) ? argv[1] : "abc"; + + ret = wc_InitBlake2b(&b2b, DIGEST_SZ); + if (ret != 0) { + printf("wc_InitBlake2b failed %d\n", ret); + return 1; + } + + /* Data may be added in as many update calls as needed. */ + ret = wc_Blake2bUpdate(&b2b, (const byte*)msg, (word32)strlen(msg)); + if (ret != 0) { + printf("wc_Blake2bUpdate failed %d\n", ret); + return 1; + } + + ret = wc_Blake2bFinal(&b2b, digest, DIGEST_SZ); + if (ret != 0) { + printf("wc_Blake2bFinal failed %d\n", ret); + return 1; + } + + print_hex("BLAKE2b-512", digest, DIGEST_SZ); + + if (argc <= 1) { + if (memcmp(digest, kat_abc, DIGEST_SZ) != 0) { + printf("Digest does not match RFC 7693 test vector!\n"); + return 1; + } + printf("Digest matches RFC 7693 test vector\n"); + } + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-blake2\n"); + return 0; +} + +#endif /* HAVE_BLAKE2B */ diff --git a/hash/blake2/blake2s-hash.c b/hash/blake2/blake2s-hash.c new file mode 100644 index 000000000..d5ce68c88 --- /dev/null +++ b/hash/blake2/blake2s-hash.c @@ -0,0 +1,99 @@ +/* blake2s-hash.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 incremental BLAKE2s hashing with the wc_Blake2s API. */ + +#include +#include + +#include +#include +#include + +#ifdef HAVE_BLAKE2S + +#define DIGEST_SZ 32 + +/* BLAKE2s-256("abc") from RFC 7693 Appendix B. */ +static const byte kat_abc[DIGEST_SZ] = { + 0x50, 0x8c, 0x5e, 0x8c, 0x32, 0x7c, 0x14, 0xe2, + 0xe1, 0xa7, 0x2b, 0xa3, 0x4e, 0xeb, 0x45, 0x2f, + 0x37, 0x45, 0x8b, 0x20, 0x9e, 0xd6, 0x3a, 0x29, + 0x4d, 0x99, 0x9b, 0x4c, 0x86, 0x67, 0x59, 0x82 +}; + +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(int argc, char* argv[]) +{ + int ret; + Blake2s b2s; + byte digest[DIGEST_SZ]; + const char* msg = (argc > 1) ? argv[1] : "abc"; + + ret = wc_InitBlake2s(&b2s, DIGEST_SZ); + if (ret != 0) { + printf("wc_InitBlake2s failed %d\n", ret); + return 1; + } + + ret = wc_Blake2sUpdate(&b2s, (const byte*)msg, (word32)strlen(msg)); + if (ret != 0) { + printf("wc_Blake2sUpdate failed %d\n", ret); + return 1; + } + + ret = wc_Blake2sFinal(&b2s, digest, DIGEST_SZ); + if (ret != 0) { + printf("wc_Blake2sFinal failed %d\n", ret); + return 1; + } + + print_hex("BLAKE2s-256", digest, DIGEST_SZ); + + if (argc <= 1) { + if (memcmp(digest, kat_abc, DIGEST_SZ) != 0) { + printf("Digest does not match RFC 7693 test vector!\n"); + return 1; + } + printf("Digest matches RFC 7693 test vector\n"); + } + + return 0; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-blake2s\n"); + return 0; +} + +#endif /* HAVE_BLAKE2S */ diff --git a/pk/hpke/Makefile b/pk/hpke/Makefile index e880cb384..3ccc59d7c 100644 --- a/pk/hpke/Makefile +++ b/pk/hpke/Makefile @@ -3,14 +3,20 @@ WOLFSSL_INSTALL_DIR=/usr/local CFLAGS= -I$(WOLFSSL_INSTALL_DIR)/include -Wall LIBS= -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl +all: hpke_test hpke_context + hpke_test: hpke_test.o $(CC) -o $@ $^ $(CFLAGS) $(LIBS) -.PHONY: clean check +hpke_context: hpke_context.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean check all clean: - rm -f *.o hpke_test + rm -f *.o hpke_test hpke_context -check: hpke_test +check: all out=$$(./hpke_test) && printf '%s' "$$out" | grep -q 'HPKE test success' + out=$$(./hpke_context) && printf '%s' "$$out" | grep -q 'HPKE context test success' @echo "PASS: pk-hpke checks" diff --git a/pk/hpke/README.md b/pk/hpke/README.md index a5fd384c4..fef875efa 100644 --- a/pk/hpke/README.md +++ b/pk/hpke/README.md @@ -1,9 +1,22 @@ -# HPKE Example with all supported options +# HPKE Examples -To build wolfSSL for this example run `./configure --enable-hpke --enable-aesgcm --enable-curve25519 --enable-ecc && make && sudo make install` +Demonstrates HPKE (Hybrid Public Key Encryption, RFC 9180): public-key +encryption built from a key encapsulation mechanism (KEM), a key derivation +function (KDF), and an authenticated cipher (AEAD). + +To build wolfSSL for these examples run `./configure --enable-hpke --enable-aesgcm --enable-curve25519 --enable-ecc && make && sudo make install` + +* `hpke_test.c` - one-shot seal/open (`wc_HpkeSealBase()` / `wc_HpkeOpenBase()`) + with all supported KEM/KDF/AEAD combinations. +* `hpke_context.c` - seal/open contexts (`wc_HpkeInitSealContext()` / + `wc_HpkeContextSealBase()` and the open equivalents): one key encapsulation + protecting an ordered sequence of messages. ```sh make ./hpke_test HPKE test success +./hpke_context +... +HPKE context test success ``` diff --git a/pk/hpke/hpke_context.c b/pk/hpke/hpke_context.c new file mode 100644 index 000000000..f1cb503c2 --- /dev/null +++ b/pk/hpke/hpke_context.c @@ -0,0 +1,294 @@ +/* hpke_context.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 HPKE (RFC 9180) context reuse: one KEM encapsulation protecting a + * whole sequence of messages. + * + * wc_HpkeSealBase() runs a fresh key encapsulation for every message it + * protects. With a seal/open context the encapsulation happens once and each + * message consumes the next AEAD nonce in the sequence, so both sides must + * process the messages in the same order. + * + * Roles in this example: + * Receiver: owns the long term HPKE key pair and publishes the public half. + * The private half never leaves them. + * Sender: looks up the receiver's public key, makes an ephemeral key pair, + * derives a seal context from the two and seals each message. + * + * Everything that crosses the wire lives in the Message struct: the serialized + * ephemeral public key (the KEM encapsulation) plus the ciphertexts. */ + +#include +#include + +#include +#include +#include +#include +#include + +#if defined(HAVE_HPKE) && (defined(HAVE_ECC) || defined(HAVE_CURVE25519)) && \ + defined(HAVE_AESGCM) + +#define NUM_MSGS 3 +#define TAG_SZ 16 /* AES-GCM authentication tag */ +#define MAX_MSG 64 + +static const char* msgs[NUM_MSGS] = { + "first message", + "second message", + "third message" +}; + +static const char* info = "hpke context example"; +static const char* aad = "message aad"; + +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; + int i; + + WC_RNG rng; + int rngInit = 0; + + /* Every struct below is declared before the first "goto exit" so that the + * cleanup at the bottom always sees initialised members. */ + struct { + Hpke suite; /* KEM/KDF/AEAD ids, agreed in advance */ + void* staticKey; /* long term pair, private half kept */ + HpkeBaseContext openCtx; + byte plain[MAX_MSG]; + } Receiver = {0}; + + struct { + Hpke suite; /* same suite, separate instance */ + void* receiverKey; /* Sender view: public key only */ + void* ephemeralKey; /* fresh per conversation */ + HpkeBaseContext sealCtx; + } Sender = {0}; + + struct { + byte receiverPubKey[HPKE_Npk_MAX]; /* what Receiver publishes */ + word16 receiverPubKeySz; + } Directory = {0}; + + struct { + byte ephemeralPubKey[HPKE_Npk_MAX]; /* the KEM encapsulation */ + word16 ephemeralPubKeySz; + byte cipher[NUM_MSGS][MAX_MSG + TAG_SZ]; + word32 cipherSz[NUM_MSGS]; /* body length; the tag follows it */ + } Message = {0}; + + /* --- Setup Receiver --- */ + /* Curve25519 when available, otherwise P-256. Both sides must pick the + * same triple or the key schedule will not line up. */ +#if defined(HAVE_CURVE25519) + ret = wc_HpkeInit(&Receiver.suite, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#else + ret = wc_HpkeInit(&Receiver.suite, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#endif + if (ret != 0) {printf("Receiver could not init HPKE suite\n"); goto exit;} + /* --- Setup Receiver --- */ + + /* --- Setup Sender --- */ +#if defined(HAVE_CURVE25519) + ret = wc_HpkeInit(&Sender.suite, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#else + ret = wc_HpkeInit(&Sender.suite, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#endif + if (ret != 0) {printf("Sender could not init HPKE suite\n"); goto exit;} + /* --- Setup Sender --- */ + + /* --- One rng instance for simplicity -- */ + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + /* --- One rng instance for simplicity -- */ + + /* --- Receiver publishes a static public key --- */ + { + /* - Make the long term pair - */ + ret = wc_HpkeGenerateKeyPair(&Receiver.suite, &Receiver.staticKey, + &rng); + if (ret != 0) {printf("Receiver could not make key pair\n"); goto exit;} + /* - Make the long term pair - */ + + /* - Publish the public half (Simulate a key directory) - */ + Directory.receiverPubKeySz = (word16)sizeof(Directory.receiverPubKey); + ret = wc_HpkeSerializePublicKey(&Receiver.suite, Receiver.staticKey, + Directory.receiverPubKey, &Directory.receiverPubKeySz); + if (ret != 0) {printf("Could not serialize receiver key\n"); goto exit;} + printf("Receiver: static HPKE key pair made and published\n"); + /* - Publish the public half (Simulate a key directory) - */ + } + /* --- Receiver publishes a static public key --- */ + + /* --- Sender looks up the Receiver and makes an ephemeral key --- */ + { + /* - Sender only ever holds the public key - */ + ret = wc_HpkeDeserializePublicKey(&Sender.suite, &Sender.receiverKey, + Directory.receiverPubKey, Directory.receiverPubKeySz); + if (ret != 0) {printf("Could not import receiver key\n"); goto exit;} + /* - Sender only ever holds the public key - */ + + /* - Fresh ephemeral pair, one per conversation - */ + ret = wc_HpkeGenerateKeyPair(&Sender.suite, &Sender.ephemeralKey, &rng); + if (ret != 0) { + printf("Sender could not make ephemeral key\n"); + goto exit; + } + /* - Fresh ephemeral pair, one per conversation - */ + } + /* --- Sender looks up the Receiver and makes an ephemeral key --- */ + + /* --- Sender Creates Messages --- */ + { + /* - Encapsulate once; the context carries the nonce sequence - */ + ret = wc_HpkeInitSealContext(&Sender.suite, &Sender.sealCtx, + Sender.ephemeralKey, Sender.receiverKey, (byte*)info, + (word32)strlen(info)); + if (ret != 0) {printf("Could not init seal context\n"); goto exit;} + /* - Encapsulate once; the context carries the nonce sequence - */ + + /* - Seal each message in order - */ + for (i = 0; i < NUM_MSGS; i++) { + Message.cipherSz[i] = (word32)strlen(msgs[i]); + if (Message.cipherSz[i] > MAX_MSG) { + printf("message %d too long for buffer\n", i); + ret = BUFFER_E; + goto exit; + } + ret = wc_HpkeContextSealBase(&Sender.suite, &Sender.sealCtx, + (byte*)aad, (word32)strlen(aad), (byte*)msgs[i], + Message.cipherSz[i], Message.cipher[i]); + if (ret != 0) {printf("Could not seal message %d\n", i); goto exit;} + printf("sealed message %d (%u bytes)\n", i, + (unsigned int)Message.cipherSz[i]); + } + /* - Seal each message in order - */ + + /* - Only the ephemeral public key travels alongside the ciphertexts - */ + Message.ephemeralPubKeySz = (word16)sizeof(Message.ephemeralPubKey); + ret = wc_HpkeSerializePublicKey(&Sender.suite, Sender.ephemeralKey, + Message.ephemeralPubKey, &Message.ephemeralPubKeySz); + if (ret != 0) {printf("Could not serialize ephemeral key\n"); goto exit;} + print_hex("KEM encapsulation", Message.ephemeralPubKey, + Message.ephemeralPubKeySz); + /* - Only the ephemeral public key travels alongside the ciphertexts - */ + + /* - Messages are ready to send - */ + } + /* --- Sender Creates Messages --- */ + + /* --- Receiver opens the messages --- */ + { + /* - Decapsulate once with the private half - */ + ret = wc_HpkeInitOpenContext(&Receiver.suite, &Receiver.openCtx, + Receiver.staticKey, Message.ephemeralPubKey, + Message.ephemeralPubKeySz, (byte*)info, (word32)strlen(info)); + if (ret != 0) {printf("Could not init open context\n"); goto exit;} + /* - Decapsulate once with the private half - */ + + /* - Open in the same order the Sender sealed - */ + for (i = 0; i < NUM_MSGS; i++) { + memset(Receiver.plain, 0, sizeof(Receiver.plain)); + ret = wc_HpkeContextOpenBase(&Receiver.suite, &Receiver.openCtx, + (byte*)aad, (word32)strlen(aad), Message.cipher[i], + Message.cipherSz[i], Receiver.plain); + if (ret != 0) {printf("Could not open message %d\n", i); goto exit;} + + if (memcmp(Receiver.plain, msgs[i], Message.cipherSz[i]) != 0) { + printf("message %d mismatch\n", i); + ret = -1; + goto exit; + } + printf("opened message %d: %.*s\n", i, (int)Message.cipherSz[i], + Receiver.plain); + } + /* - Open in the same order the Sender sealed - */ + } + /* --- Receiver opens the messages --- */ + + /* --- A replay lands on the wrong nonce and is rejected --- */ + ret = wc_HpkeContextOpenBase(&Receiver.suite, &Receiver.openCtx, + (byte*)aad, (word32)strlen(aad), Message.cipher[0], + Message.cipherSz[0], Receiver.plain); + if (ret == 0) { + printf("out-of-order open succeeded unexpectedly\n"); + ret = -1; + goto exit; + } + printf("out-of-order open rejected as expected\n"); + ret = 0; + /* --- A replay lands on the wrong nonce and is rejected --- */ + + printf("HPKE context test success\n"); + +exit: + if (ret != 0) + printf("HPKE context test error %d: %s\n", ret, + wc_GetErrorString(ret)); + + if (Sender.ephemeralKey != NULL) + wc_HpkeFreeKey(&Sender.suite, Sender.suite.kem, Sender.ephemeralKey, + NULL); + if (Sender.receiverKey != NULL) + wc_HpkeFreeKey(&Sender.suite, Sender.suite.kem, Sender.receiverKey, + NULL); + + if (Receiver.staticKey != NULL) + wc_HpkeFreeKey(&Receiver.suite, Receiver.suite.kem, Receiver.staticKey, + NULL); + + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please build wolfssl with ./configure --enable-hpke " + "--enable-aesgcm --enable-curve25519 --enable-ecc\n"); + return 0; +} + +#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */ diff --git a/pk/mikey-sakke/Makefile b/pk/mikey-sakke/Makefile new file mode 100644 index 000000000..b495bcb29 --- /dev/null +++ b/pk/mikey-sakke/Makefile @@ -0,0 +1,16 @@ +CC=gcc +WOLFSSL_INSTALL_DIR=/usr/local +CFLAGS=-Wall -I$(WOLFSSL_INSTALL_DIR)/include +LIBS=-L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl -lm + +mikey-sakke: mikey-sakke.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY: clean check + +clean: + rm -f *.o mikey-sakke + +check: mikey-sakke + out=$$(./mikey-sakke) && printf '%s' "$$out" | grep -q 'Shared Secret Values match' + @echo "PASS: pk-mikey-sakke checks" diff --git a/pk/mikey-sakke/README.md b/pk/mikey-sakke/README.md new file mode 100644 index 000000000..e55eeff13 --- /dev/null +++ b/pk/mikey-sakke/README.md @@ -0,0 +1,44 @@ +# wolfSSL MIKEY-SAKKE Example + +Demonstrates the identity-based crypto behind MIKEY-SAKKE (Multimedia +Internet KEYing with Sakai-Kasahara Key Encryption, RFC 6509), the key +exchange used by secure-voice systems such as 3GPP Mission Critical Push To +Talk: + +* ECCSI (Elliptic Curve-based Certificateless Signatures for Identity-based + encryption, RFC 6507) - identity-based signatures (`wc_*Eccsi*`) +* SAKKE (Sakai-Kasahara Key Encryption, RFC 6508) - identity-based key + encapsulation (`wc_*Sakke*`) + +In identity-based crypto there are no per-user certificates: a user's public +key is their identity string (phone number, email). A Key Management Service +(KMS) holds master secrets and provisions each user's private material out of +band. + +The example runs the whole flow in one program: + +1. KMS creates master ECCSI and SAKKE keys. +2. KMS provisions Alice's ECCSI signing pair - Secret Signing Key (SSK) and + Public Validation Token (PVT) - for her identity, and Bob's SAKKE + Receiver Secret Key (RSK) for his. +3. Alice generates a 128-bit Shared Secret Value, encapsulates it to Bob's + identity, and ECCSI-signs the payload. +4. Bob verifies the signature against Alice's identity, derives the SSV with + his RSK, and both sides end up with the same session key. + +## Building wolfSSL + +``` +./configure --enable-eccsi --enable-sakke +make +sudo make install +``` + +## Building and running the example + +``` +make +./mikey-sakke +``` + +Expected output ends with `Shared Secret Values match`. diff --git a/pk/mikey-sakke/mikey-sakke.c b/pk/mikey-sakke/mikey-sakke.c new file mode 100644 index 000000000..cf28587e6 --- /dev/null +++ b/pk/mikey-sakke/mikey-sakke.c @@ -0,0 +1,483 @@ +/* mikey-sakke.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 the MIKEY-SAKKE identity-based key exchange (RFC 6507-6509): + * ECCSI signatures plus SAKKE key encapsulation. + * + * In identity-based crypto there are no per-user certificates: a user's + * public key IS their identity (e.g. a phone number or email). A Key + * Management Service (KMS) holds master secrets and provisions each user's + * key material out of band. + * + * Roles in this example: + * KMS: makes master ECCSI/SAKKE keys, provisions Alice's ECCSI signing + * pair (SSK, PVT) and Bob's SAKKE Receiver Secret Key (RSK). + * Alice: generates a Shared Secret Value (SSV), encapsulates it to Bob's + * identity, and signs the payload with ECCSI (RFC 6509 pattern). + * Bob: verifies Alice's ECCSI signature and derives the SSV with his + * RSK. Both then hold the same SSV for use as a session key. */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(WOLFCRYPT_HAVE_ECCSI) && defined(WOLFCRYPT_HAVE_SAKKE) + +#define SSV_SZ 16 +#define AUTH_SZ 257 +#define ECCSI_SIG_SZ 129 +#define ECCSI_PUB_KEY_SZ (32 * 2) /* raw P-256 point: X || Y */ +#define SAKKE_PUB_KEY_SZ (128 * 2) /* raw 1024-bit point: X || Y */ +#define MAX_ID_SZ 64 + +static const byte aliceId[] = "alice@example.com"; +static const byte bobId[] = "bob@example.com"; + +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; + + WC_RNG rng; + int rngInit = 0; + + /* Every struct below is declared before the first "goto exit" so that the + * cleanup at the bottom always sees initialised members. */ + struct { + EccsiKey kmsEccsi; /* KMS master signing key */ + int kmsEccsiInit; + SakkeKey kmsSakke; /* KMS master encryption key */ + int kmsSakkeInit; + } kms = {0}; + + struct { + byte kmsAuthPublicKey[ECCSI_PUB_KEY_SZ]; /* KMS ECCSI public key */ + word32 kmsAuthPublicKeySz; + byte kmsSakkePublicKey[SAKKE_PUB_KEY_SZ]; /* KMS SAKKE public key */ + word32 kmsSakkePublicKeySz; + } KmsCertficate = {0}; + + struct { + char senderId[MAX_ID_SZ]; + byte payload[SSV_SZ + AUTH_SZ]; /* encapsulated SSV || auth */ + word16 authSz; + byte signature[ECCSI_SIG_SZ]; + word32 signatureSz; + } Message = {0}; + + struct { + EccsiKey publicKeyEccsi; /* Alice view: KMS public key only */ + int publicKeyEccsiInit; + SakkeKey publicKeySakke; /* Alice view: KMS public key only */ + int publicKeySakkeInit; + mp_int secretSigningKey; + int secretSigningKeyInit; + ecc_point* publicValidationToken; + ecc_point* receiverSecretKey; + byte sharedSecretValue[SSV_SZ]; /* plaintext SSV (Alices's copy) */ + word16 sharedSecretValueSz; + int verified; + char* id; + } Alice = {0}; + + struct { + EccsiKey publicKeyEccsi; /* Bobs view: KMS public key only */ + int publicKeyEccsiInit; + SakkeKey publicKeySakke; /* Bob view: KMS public key only */ + int publicKeySakkeInit; + mp_int secretSigningKey; + int secretSigningKeyInit; + ecc_point* publicValidationToken; + ecc_point* receiverSecretKey; + byte dirived_sharedSecretValue[SSV_SZ]; /* plaintext SSV (Bob's copy) */ + word16 dirived_sharedSecretValueSz; + int verified; + char* id; + } Bob = {0}; + + /* --- Setup KMS --- */ + ret = wc_InitSakkeKey(&kms.kmsSakke, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else kms.kmsSakkeInit = 1; + ret = wc_InitEccsiKey(&kms.kmsEccsi, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else kms.kmsEccsiInit = 1; + /* --- Setup KMS --- */ + + /* --- Init Alice --- */ + Alice.id = (char*)aliceId; + ret = wc_InitEccsiKey(&Alice.publicKeyEccsi, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else Alice.publicKeyEccsiInit = 1; + ret = wc_InitSakkeKey(&Alice.publicKeySakke, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else Alice.publicKeySakkeInit = 1; + ret = mp_init(&Alice.secretSigningKey); + if (ret != 0) goto exit; else Alice.secretSigningKeyInit = 1; + Alice.publicValidationToken = wc_ecc_new_point(); + Alice.receiverSecretKey = wc_ecc_new_point(); + if (Alice.publicValidationToken == NULL || Alice.receiverSecretKey == NULL) + {ret = MEMORY_E; goto exit;} + /* --- Init Alice --- */ + + /* --- Init Bob --- */ + Bob.id = (char*)bobId; + ret = wc_InitEccsiKey(&Bob.publicKeyEccsi, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else Bob.publicKeyEccsiInit = 1; + ret = wc_InitSakkeKey(&Bob.publicKeySakke, NULL, INVALID_DEVID); + if (ret != 0) goto exit; else Bob.publicKeySakkeInit = 1; + ret = mp_init(&Bob.secretSigningKey); + if (ret != 0) goto exit; else Bob.secretSigningKeyInit = 1; + Bob.publicValidationToken = wc_ecc_new_point(); + Bob.receiverSecretKey = wc_ecc_new_point(); + if (Bob.publicValidationToken == NULL || Bob.receiverSecretKey == NULL) + {ret = MEMORY_E; goto exit;} + /* --- Init Bob --- */ + + + + /* --- One rng instance for simplicity -- */ + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + /* --- One rng instance for simplicity -- */ + + + /* --- KMS setup: master keys --- */ + { + /* - KMS setup: eccsi keys - */ + ret = wc_MakeEccsiKey(&kms.kmsEccsi, &rng); + if (ret != 0) { + printf("wc_MakeEccsiKey failed %d\n", ret); + goto exit; + } + /* - KMS setup: eccsi keys - */ + + /* - KMS setup: sakke keys - */ + ret = wc_MakeSakkeKey(&kms.kmsSakke, &rng); + if (ret != 0) { + printf("wc_MakeSakkeKey failed %d\n", ret); + goto exit; + } + printf("KMS: master ECCSI and SAKKE keys made\n"); + /* - KMS setup: sakke keys - */ + } + /* --- KMS setup: master keys --- */ + + + /* --- Enroll Alice with KMS to get their keys --- */ + { + /* - Get PublicKeys from KMS (Simulate KMS sending pubkeys only) - */ + KmsCertficate.kmsAuthPublicKeySz = ECCSI_PUB_KEY_SZ; + ret = wc_ExportEccsiPublicKey(&kms.kmsEccsi, + KmsCertficate.kmsAuthPublicKey, + &KmsCertficate.kmsAuthPublicKeySz, 1); + + if (ret != 0) { + printf("could not export pub eccsi key from KMS"); + goto exit; + } + + KmsCertficate.kmsSakkePublicKeySz = SAKKE_PUB_KEY_SZ; + ret = wc_ExportSakkePublicKey(&kms.kmsSakke, + KmsCertficate.kmsSakkePublicKey, + &KmsCertficate.kmsSakkePublicKeySz, 1); + if (ret != 0) { + printf("could not export pub sakke key from KMS"); + goto exit; + } + /* - Get PublicKeys from KMS (Simulate KMS sending pubkeys only) - */ + + /* - Save public key from KMS - */ + ret = wc_ImportEccsiPublicKey(&Alice.publicKeyEccsi, + KmsCertficate.kmsAuthPublicKey, + KmsCertficate.kmsAuthPublicKeySz, 1); + if (ret == 0) + ret = wc_ImportSakkePublicKey(&Alice.publicKeySakke, + KmsCertficate.kmsSakkePublicKey, + KmsCertficate.kmsSakkePublicKeySz, 1); + if (ret != 0) {printf("Unable to transfer kms public keys"); goto exit;} + /* - Save public key from KMS - */ + + /* - Get Signing pair from KMS - */ + ret = wc_MakeEccsiPair(&kms.kmsEccsi, &rng, WC_HASH_TYPE_SHA256, + (byte*)Alice.id, sizeof(aliceId), &Alice.secretSigningKey, + Alice.publicValidationToken); + if (ret != 0) {printf("Unable to make signing pairs"); goto exit;} + /* - Get Sining pair from KMS - */ + + /* - Get Issue Recivier Key - */ + ret = wc_MakeSakkeRsk(&kms.kmsSakke, (byte*)Alice.id, + sizeof(aliceId), Alice.receiverSecretKey); + if (ret != 0) {printf("Unable to make receiver secret key"); goto exit;} + /* - Get Issue Recivier Key - */ + } + /* --- Enroll Alice with KMS to get their keys --- */ + + /* --- Reset Kms Cert for Bob --- */ + memset(&KmsCertficate, 0, sizeof(KmsCertficate)); + /* --- Reset Kms Cert for Bob --- */ + + /* --- Enroll Bob with KMS to get their keys --- */ + { + /* - Get PublicKeys from KMS (Simulate KMS sending pubkeys only) - */ + KmsCertficate.kmsAuthPublicKeySz = ECCSI_PUB_KEY_SZ; + ret = wc_ExportEccsiPublicKey(&kms.kmsEccsi, + KmsCertficate.kmsAuthPublicKey, + &KmsCertficate.kmsAuthPublicKeySz, 1); + + if (ret != 0) { + printf("could not export pub eccsi key from KMS"); + goto exit; + } + + KmsCertficate.kmsSakkePublicKeySz = SAKKE_PUB_KEY_SZ; + ret = wc_ExportSakkePublicKey(&kms.kmsSakke, + KmsCertficate.kmsSakkePublicKey, + &KmsCertficate.kmsSakkePublicKeySz, 1); + if (ret != 0) { + printf("could not export pub sakke key from KMS"); + goto exit; + } + /* - Get PublicKeys from KMS (Simulate KMS sending pubkeys only) - */ + + /* - Save public key from KMS - */ + ret = wc_ImportEccsiPublicKey(&Bob.publicKeyEccsi, + KmsCertficate.kmsAuthPublicKey, + KmsCertficate.kmsAuthPublicKeySz, 1); + if (ret == 0) + ret = wc_ImportSakkePublicKey(&Bob.publicKeySakke, + KmsCertficate.kmsSakkePublicKey, + KmsCertficate.kmsSakkePublicKeySz, 1); + if (ret != 0) {printf("Unable to transfer kms public keys"); goto exit;} + /* - Save public key from KMS - */ + + /* - Get Signing pair from KMS - */ + ret = wc_MakeEccsiPair(&kms.kmsEccsi, &rng, WC_HASH_TYPE_SHA256, + (byte*)Bob.id, sizeof(bobId), &Bob.secretSigningKey, + Bob.publicValidationToken); + if (ret != 0) {printf("Unable to make signing pairs"); goto exit;} + /* - Get Sining pair from KMS - */ + + /* - Get Issue Recivier Key - */ + ret = wc_MakeSakkeRsk(&kms.kmsSakke, (byte*)Bob.id, + sizeof(bobId), Bob.receiverSecretKey); + if (ret != 0) {printf("Unable to make receiver secret key"); goto exit;} + /* - Get Issue Recivier Key - */ + } + /* --- Enroll Bob with KMS to get their keys --- */ + + /* --- Alice Creates Message --- */ + { + byte hashId[WC_MAX_DIGEST_SIZE]; + byte hashIdSz = 0; + memcpy(Message.senderId, Alice.id, sizeof(aliceId)); + + /* - We are handwaving that alice know Bobs Id - */ + ret = wc_SetSakkeIdentity(&Alice.publicKeySakke, (byte*)Bob.id, + sizeof(bobId)); + if (ret != 0) {printf("Could not set Sakkee id"); goto exit;} + /* - We are handwaving that alice know Bobs Id - */ + + /* - Create SSV - */ + /* Size in is the buffer size wanted; wc_GenerateSakkeSSV rejects 0. */ + Alice.sharedSecretValueSz = SSV_SZ; + ret = wc_GenerateSakkeSSV(&Alice.publicKeySakke, &rng, + Alice.sharedSecretValue, &Alice.sharedSecretValueSz); + if (ret != 0) {printf("Could not generate SSV"); goto exit;} + /* - Create SSV - */ + + /* - Encapsulate SSV in place - */ + memcpy(Message.payload, Alice.sharedSecretValue, + Alice.sharedSecretValueSz); + Message.authSz = AUTH_SZ; + ret = wc_MakeSakkeEncapsulatedSSV(&Alice.publicKeySakke, + WC_HASH_TYPE_SHA256, Message.payload, Alice.sharedSecretValueSz, + Message.payload + Alice.sharedSecretValueSz, &Message.authSz); + if (ret != 0) {printf("Could not encapsulate SSV"); goto exit;} + /* - Encapsulate SSV in place - */ + + /* - Hash Alices Id - */ + ret = wc_HashEccsiId(&Alice.publicKeyEccsi, WC_HASH_TYPE_SHA256, + (byte*)Alice.id, sizeof(aliceId), Alice.publicValidationToken, + hashId, &hashIdSz); + /* - Hash Alices Id - */ + + /* - Load data in to Eccsi Object - */ + if (ret == 0) + ret = wc_SetEccsiHash(&Alice.publicKeyEccsi, hashId, hashIdSz); + if (ret == 0) + ret = wc_SetEccsiPair(&Alice.publicKeyEccsi, + &Alice.secretSigningKey, Alice.publicValidationToken); + /* - Load data in to Eccsi Object - */ + + /* - Sign the Eccsi Hash - */ + if (ret == 0) { + Message.signatureSz = (word32)sizeof(Message.signature); + ret = wc_SignEccsiHash(&Alice.publicKeyEccsi, &rng, + WC_HASH_TYPE_SHA256, Message.payload, + Alice.sharedSecretValueSz + Message.authSz, + Message.signature, &Message.signatureSz); + } + /* - Sign the Eccsi Hash - */ + + if (ret != 0) {printf("Unable to sign payload"); goto exit;} + } + /* - Message is ready to send - */ + /* --- Alice Creates Message --- */ + + /* --- Bob recives message --- */ + + /* --- Bob extracts info from message --- */ + { + ecc_point* senderPvt; + byte hashId[WC_MAX_DIGEST_SIZE]; + byte hashIdSz = 0; + int verified = 0; + + /* Bob signs/derives over the same SSV length Alice used. */ + Bob.dirived_sharedSecretValueSz = SSV_SZ; + + senderPvt = wc_ecc_new_point(); + if (senderPvt == NULL) {ret = MEMORY_E; goto exit;} + /* - Get Sender Public Validation Token - */ + ret = wc_DecodeEccsiPvtFromSig(&Bob.publicKeyEccsi, + Message.signature, Message.signatureSz, senderPvt); + if (ret != 0) {printf("Could not Decode Pvt."); goto BobFail;} + /* - Get Sender Public Validation Token - */ + + /* - Verify the Message - */ + ret = wc_HashEccsiId(&Bob.publicKeyEccsi, WC_HASH_TYPE_SHA256, + (byte*)Message.senderId, sizeof(aliceId), senderPvt, hashId, + &hashIdSz); + if (ret != 0) {printf("Could not Hash Sender Id."); goto BobFail;} + ret = wc_SetEccsiHash(&Bob.publicKeyEccsi, hashId, hashIdSz); + if (ret != 0) {printf("Could not Set Hash."); goto BobFail;} + ret = wc_VerifyEccsiHash(&Bob.publicKeyEccsi, WC_HASH_TYPE_SHA256, + Message.payload, + Bob.dirived_sharedSecretValueSz + Message.authSz, + Message.signature, Message.signatureSz, &verified); + /* A bad signature is reported through "verified", not through ret. */ + if (ret == 0 && !verified) ret = SIG_VERIFY_E; + if (ret != 0) {printf("Could not Verify Message."); goto BobFail;} + /* - Verify the Message - */ + + /* - Get The Shared secret value out of the Message - */ + ret = wc_SetSakkeIdentity(&Bob.publicKeySakke, (const byte*)Bob.id, + sizeof(bobId)); + if (ret != 0) {printf("Could not Sakke Id."); goto BobFail;} + ret = wc_SetSakkeRsk(&Bob.publicKeySakke, Bob.receiverSecretKey, + NULL, 0); + if (ret != 0) {printf("Could Set Sakke Rsk."); goto BobFail;} + memcpy(Bob.dirived_sharedSecretValue, Message.payload, + Bob.dirived_sharedSecretValueSz); + ret = wc_DeriveSakkeSSV(&Bob.publicKeySakke, WC_HASH_TYPE_SHA256, + Bob.dirived_sharedSecretValue, Bob.dirived_sharedSecretValueSz, + Message.payload + Bob.dirived_sharedSecretValueSz, + Message.authSz); + if (ret != 0) {printf("Could not derive Sakke SSV."); goto BobFail;} + /* - Get The Shared secret value out of the Message - */ + + /* - Error - */ + if (0) { +BobFail: + wc_ecc_del_point(senderPvt); + goto exit; + } + wc_ecc_del_point(senderPvt); + } + + + if (memcmp(Alice.sharedSecretValue, Bob.dirived_sharedSecretValue, + SSV_SZ) != 0) { + printf("SSVs differ!\n"); + ret = -1; + goto exit; + } + print_hex("Shared Secret Value", Alice.sharedSecretValue, SSV_SZ); + printf("Shared Secret Values match\n"); + ret = 0; + +exit: + if (ret != 0) + printf("error %d: %s\n", ret, wc_GetErrorString(ret)); + + if (Bob.receiverSecretKey != NULL) + wc_ecc_forcezero_point(Bob.receiverSecretKey); + if (Bob.publicValidationToken != NULL) + wc_ecc_del_point(Bob.publicValidationToken); + if (Bob.receiverSecretKey != NULL) + wc_ecc_del_point(Bob.receiverSecretKey); + if (Bob.secretSigningKeyInit) + mp_forcezero(&Bob.secretSigningKey); + if (Bob.publicKeySakkeInit) + wc_FreeSakkeKey(&Bob.publicKeySakke); + if (Bob.publicKeyEccsiInit) + wc_FreeEccsiKey(&Bob.publicKeyEccsi); + + if (Alice.receiverSecretKey != NULL) + wc_ecc_forcezero_point(Alice.receiverSecretKey); + if (Alice.publicValidationToken != NULL) + wc_ecc_del_point(Alice.publicValidationToken); + if (Alice.receiverSecretKey != NULL) + wc_ecc_del_point(Alice.receiverSecretKey); + if (Alice.secretSigningKeyInit) + mp_forcezero(&Alice.secretSigningKey); + if (Alice.publicKeySakkeInit) + wc_FreeSakkeKey(&Alice.publicKeySakke); + if (Alice.publicKeyEccsiInit) + wc_FreeEccsiKey(&Alice.publicKeyEccsi); + + if (kms.kmsSakkeInit) + wc_FreeSakkeKey(&kms.kmsSakke); + if (kms.kmsEccsiInit) + wc_FreeEccsiKey(&kms.kmsEccsi); + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-eccsi " + "--enable-sakke\n"); + return 0; +} + +#endif /* WOLFCRYPT_HAVE_ECCSI && WOLFCRYPT_HAVE_SAKKE */ diff --git a/pk/srp/Makefile b/pk/srp/Makefile index b2d0def47..ed12cdf58 100644 --- a/pk/srp/Makefile +++ b/pk/srp/Makefile @@ -4,7 +4,7 @@ CFLAGS=-Wall -I$(WOLFSSL_INSTALL_DIR)/include -g #LIBS= -lwolfssl -lm LIBS= -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl -lm -all: srp srp_gen +all: srp srp_gen srp_sha256 srp.o: srp.c srp_params.h srp_store.h $(CC) -c -o $@ srp.c $(CFLAGS) @@ -12,18 +12,25 @@ srp.o: srp.c srp_params.h srp_store.h srp_gen.o: srp_gen.c srp_params.h $(CC) -c -o $@ srp_gen.c $(CFLAGS) +srp_sha256.o: srp_sha256.c srp_params_2048.h + $(CC) -c -o $@ srp_sha256.c $(CFLAGS) + srp: srp.o $(CC) -o $@ $^ $(CFLAGS) $(LIBS) srp_gen: srp_gen.o $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +srp_sha256: srp_sha256.o + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + .PHONY: clean check clean: - rm -f *.der *.x963 *.o srp srp_gen + rm -f *.der *.x963 *.o srp srp_gen srp_sha256 -check: srp srp_gen +check: srp srp_gen srp_sha256 out=$$(./srp wolfssl password) && printf '%s' "$$out" | grep -q 'Client verified server proof' out=$$(./srp_gen wolfssl password) && printf '%s' "$$out" | grep -qF 'static const byte verifier[' + out=$$(./srp_sha256) && printf '%s' "$$out" | grep -q 'Session keys match' @echo "PASS: pk-srp checks" diff --git a/pk/srp/README.md b/pk/srp/README.md index cc0de5f79..03d6b9807 100644 --- a/pk/srp/README.md +++ b/pk/srp/README.md @@ -20,3 +20,10 @@ make ``` + +srp_sha256.c runs a complete SRP-6a exchange (enrollment through mutual proof +verification) using SHA-256 and the RFC 5054 2048-bit group: + +``` +./srp_sha256 +``` diff --git a/pk/srp/srp_params_2048.h b/pk/srp/srp_params_2048.h new file mode 100644 index 000000000..a881a316d --- /dev/null +++ b/pk/srp/srp_params_2048.h @@ -0,0 +1,55 @@ +/* srp_params_2048.h + * + * 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 + */ + +#ifndef SRP_PARAMS_2048_H +#define SRP_PARAMS_2048_H + +/* 2048-bit group from RFC 5054 Appendix A. */ +static const byte srp_n_2048[] = { + 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, 0xF1, 0x66, 0xDE, 0x5E, + 0x13, 0x89, 0x58, 0x2F, 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, + 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50, 0xA3, 0x73, 0x29, 0xCB, + 0xB4, 0xA0, 0x99, 0xED, 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D, + 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D, 0xCD, 0x7F, 0x48, 0xA9, + 0xDA, 0x04, 0xFD, 0x50, 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, + 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, 0x66, 0x1A, 0x05, 0xFB, + 0xD5, 0xFA, 0xAA, 0xE8, 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, + 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA, 0xA8, 0x0D, 0x74, 0x0A, + 0xDB, 0xF4, 0xFF, 0x74, 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7, + 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B, 0xCA, 0x97, 0xB4, 0x3A, + 0x23, 0xFB, 0x80, 0x16, 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, + 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, 0x5B, 0x9D, 0x32, 0xE6, + 0x88, 0xF8, 0x77, 0x48, 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, + 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA, 0x03, 0x2C, 0xFB, 0xDB, + 0xF5, 0x2F, 0xB3, 0x78, 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6, + 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29, 0x9C, 0xCC, 0x04, 0x1C, + 0x7B, 0xC3, 0x08, 0xD8, 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, + 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, 0x94, 0xB5, 0xC8, 0x03, + 0xD8, 0x9F, 0x7A, 0xE4, 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, + 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, + 0x9E, 0x4A, 0xFF, 0x73 +}; + +static const byte srp_g_2048[] = { + 0x02 +}; + +#endif /* SRP_PARAMS_2048_H */ diff --git a/pk/srp/srp_sha256.c b/pk/srp/srp_sha256.c new file mode 100644 index 000000000..c54e2f604 --- /dev/null +++ b/pk/srp/srp_sha256.c @@ -0,0 +1,249 @@ +/* srp_sha256.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 a full SRP-6a exchange using SHA-256 and the RFC 5054 2048-bit + * group. Both sides run in this one program: + * + * enrollment: client derives a verifier from the password; server stores + * (username, salt, verifier) and never sees the password. + * login: both sides exchange public keys, compute the session key, + * and prove knowledge of it to each other. */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WOLFCRYPT_HAVE_SRP + +#include "srp_params_2048.h" + +#define SALT_SZ 16 +#define KEY_BUF_SZ 256 +#define PROOF_SZ 64 + +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; + + WC_RNG rng; + int rngInit = 0; + + /* Shared by both sides: the client picks it at enrollment and the server + * keeps it alongside the verifier. */ + byte salt[SALT_SZ]; + + /* Every struct below is declared before the first "goto exit" so that the + * cleanup at the bottom always sees initialised members. */ + struct { + Srp cli; + int cliInit; + byte clientPub[KEY_BUF_SZ]; /* A, sent to the server */ + word32 clientPubSz; + const char* username; + const char* password; /* never leaves the client */ + byte proof[PROOF_SZ]; /* M1 */ + word32 proofSz; + byte verifier[KEY_BUF_SZ]; /* derived from the password */ + word32 verifierSz; + } client = {0}; + + struct { + Srp srv; + int srvInit; + byte serverPub[KEY_BUF_SZ]; /* B, sent to the client */ + word32 serverPubSz; + const char* username; /* the stored record: no password */ + byte proof[PROOF_SZ]; /* M2 */ + word32 proofSz; + byte verifier[KEY_BUF_SZ]; + word32 verifierSz; + } server = {0}; + + client.username = "alice"; + client.password = "password123"; + + /* --- One RNG instance for simplicity --- */ + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + /* --- One RNG instance for simplicity --- */ + + /* --- Enrollment: random salt, then a client-side object derives the + * verifier the server will store. --- */ + ret = wc_RNG_GenerateBlock(&rng, salt, sizeof(salt)); + if (ret != 0) { + printf("salt generation failed %d\n", ret); + goto exit; + } + + ret = wc_SrpInit(&client.cli, SRP_TYPE_SHA256, SRP_CLIENT_SIDE); + if (ret != 0) goto exit; else client.cliInit = 1; + + ret = wc_SrpSetUsername(&client.cli, (const byte*)client.username, + (word32)strlen(client.username)); + if (ret == 0) + ret = wc_SrpSetParams(&client.cli, srp_n_2048, sizeof(srp_n_2048), + srp_g_2048, sizeof(srp_g_2048), salt, + sizeof(salt)); + if (ret == 0) + ret = wc_SrpSetPassword(&client.cli, (const byte*)client.password, + (word32)strlen(client.password)); + if (ret == 0) { + client.verifierSz = (word32)sizeof(client.verifier); + ret = wc_SrpGetVerifier(&client.cli, client.verifier, + &client.verifierSz); + } + if (ret != 0) { + printf("verifier generation failed %d\n", ret); + goto exit; + } + printf("Enrolled user '%s' (verifier %u bytes)\n", client.username, + client.verifierSz); + /* --- Enrollment --- */ + + /* --- Hand the record to the server; the password stays behind --- */ + server.username = client.username; + server.verifierSz = client.verifierSz; + memcpy(server.verifier, client.verifier, server.verifierSz); + /* --- Hand the record to the server; the password stays behind --- */ + + /* --- Login: client computes its public key A. The enrollment object is + * reused; a real client would build a fresh one the same way. --- */ + client.clientPubSz = (word32)sizeof(client.clientPub); + ret = wc_SrpGetPublic(&client.cli, client.clientPub, &client.clientPubSz); + if (ret != 0) { + printf("client wc_SrpGetPublic failed %d\n", ret); + goto exit; + } + /* --- Login: client public key A --- */ + + /* --- Server loads the stored verifier and computes its public key B --- */ + ret = wc_SrpInit(&server.srv, SRP_TYPE_SHA256, SRP_SERVER_SIDE); + if (ret != 0) goto exit; else server.srvInit = 1; + + ret = wc_SrpSetUsername(&server.srv, (const byte*)server.username, + (word32)strlen(server.username)); + if (ret == 0) + ret = wc_SrpSetParams(&server.srv, srp_n_2048, sizeof(srp_n_2048), + srp_g_2048, sizeof(srp_g_2048), salt, + sizeof(salt)); + if (ret == 0) + ret = wc_SrpSetVerifier(&server.srv, server.verifier, + server.verifierSz); + if (ret == 0) { + server.serverPubSz = (word32)sizeof(server.serverPub); + ret = wc_SrpGetPublic(&server.srv, server.serverPub, + &server.serverPubSz); + } + if (ret != 0) { + printf("server setup failed %d\n", ret); + goto exit; + } + /* --- Server public key B --- */ + + /* --- Both sides derive the session key from the two public keys --- */ + ret = wc_SrpComputeKey(&client.cli, client.clientPub, client.clientPubSz, + server.serverPub, server.serverPubSz); + if (ret == 0) + ret = wc_SrpComputeKey(&server.srv, client.clientPub, + client.clientPubSz, server.serverPub, + server.serverPubSz); + if (ret != 0) { + printf("wc_SrpComputeKey failed %d\n", ret); + goto exit; + } + /* --- Both sides derive the session key --- */ + + /* --- Client proves first; only then does the server prove back --- */ + client.proofSz = (word32)sizeof(client.proof); + ret = wc_SrpGetProof(&client.cli, client.proof, &client.proofSz); + if (ret == 0) + ret = wc_SrpVerifyPeersProof(&server.srv, client.proof, + client.proofSz); + if (ret != 0) { + printf("server rejected client proof %d\n", ret); + goto exit; + } + printf("Server verified client proof\n"); + + server.proofSz = (word32)sizeof(server.proof); + ret = wc_SrpGetProof(&server.srv, server.proof, &server.proofSz); + if (ret == 0) + ret = wc_SrpVerifyPeersProof(&client.cli, server.proof, + server.proofSz); + if (ret != 0) { + printf("client rejected server proof %d\n", ret); + goto exit; + } + printf("Client verified server proof\n"); + /* --- Client proves first; only then does the server prove back --- */ + + if (client.cli.keySz != server.srv.keySz || + memcmp(client.cli.key, server.srv.key, client.cli.keySz) != 0) { + printf("Session keys differ!\n"); + ret = -1; + goto exit; + } + print_hex("session key", client.cli.key, client.cli.keySz); + printf("Session keys match\n"); + ret = 0; + +exit: + if (ret != 0) + printf("error %d: %s\n", ret, wc_GetErrorString(ret)); + + if (server.srvInit) + wc_SrpTerm(&server.srv); + if (client.cliInit) + wc_SrpTerm(&client.cli); + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? 0 : 1; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-srp\n"); + return 0; +} + +#endif /* WOLFCRYPT_HAVE_SRP */ diff --git a/pq/slh_dsa/Makefile b/pq/slh_dsa/Makefile new file mode 100644 index 000000000..d041b2bc8 --- /dev/null +++ b/pq/slh_dsa/Makefile @@ -0,0 +1,23 @@ +CC = gcc + +WOLFSSL_INSTALL_DIR = /usr/local + +WOLFSSL_CFLAGS = -Wextra -Werror -Wall -I$(WOLFSSL_INSTALL_DIR)/include +WOLFSSL_LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lm -lwolfssl + +DEBUG_FLAGS = -g -DDEBUG + +all: slh_dsa_test + +slh_dsa_test: slh_dsa.c + $(CC) -o $@ $^ $(WOLFSSL_CFLAGS) $(WOLFSSL_LIBS) $(DEBUG_FLAGS) + +.PHONY: clean all check + +clean: + rm -f *.o slh_dsa_test + +check: slh_dsa_test + out=$$(./slh_dsa_test -s shake-128f) && printf '%s' "$$out" | grep -q 'info: verify message good' + out=$$(./slh_dsa_test -s shake-192f -m "wolfssl-examples CI") && printf '%s' "$$out" | grep -q 'info: verify message good' + @echo "PASS: pq-slh-dsa checks" diff --git a/pq/slh_dsa/README.md b/pq/slh_dsa/README.md new file mode 100644 index 000000000..f8af7e2e1 --- /dev/null +++ b/pq/slh_dsa/README.md @@ -0,0 +1,44 @@ +# wolfSSL SLH-DSA Example + +Demonstrates SLH-DSA (Stateless Hash-Based Digital Signature Algorithm, +FIPS 205, formerly SPHINCS+) key generation, signing and verification with +wolfCrypt. + +SLH-DSA's security rests only on hash function assumptions. Compared to +ML-DSA it has +much smaller keys but much larger signatures and slower signing. + +## Building wolfSSL + +``` +./configure --enable-slhdsa +make +sudo make install +``` + +`--enable-slhdsa` enables the six SHAKE parameter sets. Use +`--enable-slhdsa=yes,sha2` to also enable the SHA2 parameter sets. + +## Building and running the example + +``` +make +./slh_dsa_test [-v] [-s ] [-m ] +``` + +Parameter sets: `shake-128s`, `shake-128f`, `shake-192s`, `shake-192f`, +`shake-256s`, `shake-256f` (and `sha2-*` equivalents when enabled). The `s` +(small) variants trade signing speed for smaller signatures; the `f` (fast) +variants sign faster but produce larger signatures. Default is `shake-128f`. + +Example: + +``` +$ ./slh_dsa_test -s shake-128f +info: using SLH-DSA-shake-128f: pub 32 bytes, priv 64 bytes, sig 17088 bytes +info: making key +info: signing message +info: verify message good +info: corrupted signature rejected as expected +info: done +``` diff --git a/pq/slh_dsa/slh_dsa.c b/pq/slh_dsa/slh_dsa.c new file mode 100644 index 000000000..4fa065be1 --- /dev/null +++ b/pq/slh_dsa/slh_dsa.c @@ -0,0 +1,247 @@ +/* slh_dsa.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 SLH-DSA (FIPS 205) key generation, signing and verifying. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WOLFSSL_HAVE_SLHDSA + +struct param_map_t { + const char* name; + enum SlhDsaParam param; +}; + +static const struct param_map_t param_map[] = { + { "shake-128s", SLHDSA_SHAKE128S }, + { "shake-128f", SLHDSA_SHAKE128F }, + { "shake-192s", SLHDSA_SHAKE192S }, + { "shake-192f", SLHDSA_SHAKE192F }, + { "shake-256s", SLHDSA_SHAKE256S }, + { "shake-256f", SLHDSA_SHAKE256F }, +#ifdef WOLFSSL_SLHDSA_SHA2 + { "sha2-128s", SLHDSA_SHA2_128S }, + { "sha2-128f", SLHDSA_SHA2_128F }, + { "sha2-192s", SLHDSA_SHA2_192S }, + { "sha2-192f", SLHDSA_SHA2_192F }, + { "sha2-256s", SLHDSA_SHA2_256S }, + { "sha2-256f", SLHDSA_SHA2_256F }, +#endif +}; + +static const size_t param_map_sz = sizeof(param_map) / sizeof(param_map[0]); + +static void print_usage_and_die(void) +{ + size_t i; + + printf("usage:\n"); + printf(" ./slh_dsa_test [-v] [-s ] [-m ]\n"); + printf("\n"); + printf("parameter sets:\n"); + for (i = 0; i < param_map_sz; i++) + printf(" %s\n", param_map[i].name); + exit(EXIT_FAILURE); +} + +static void dump_hex(const char* what, const byte* data, word32 len) +{ + word32 i; + + printf("%s (%u bytes):\n", what, len); + for (i = 0; i < len; i++) { + printf("%02x", data[i]); + if ((i + 1) % 32 == 0) + printf("\n"); + } + if (len % 32 != 0) + printf("\n"); +} + +int main(int argc, char* argv[]) +{ + int ret; + int opt; + size_t i; + const char* paramName = "shake-128f"; + const char* msg = "wolfssl slh-dsa example"; + enum SlhDsaParam param = SLHDSA_SHAKE128F; + int verbose = 0; + SlhDsaKey key; + int keyInit = 0; + WC_RNG rng; + int rngInit = 0; + byte* sig = NULL; + byte pub[64]; + word32 pubLen = (word32)sizeof(pub); + word32 sigLen = 0; + int sigSz; + + while ((opt = getopt(argc, argv, "s:m:v?")) != -1) { + switch (opt) { + case 's': + paramName = optarg; + break; + case 'm': + msg = optarg; + break; + case 'v': + verbose = 1; + break; + default: + print_usage_and_die(); + } + } + + for (i = 0; i < param_map_sz; i++) { + if (strcmp(paramName, param_map[i].name) == 0) { + param = param_map[i].param; + break; + } + } + if (i == param_map_sz) { + printf("error: unknown parameter set: %s\n", paramName); + print_usage_and_die(); + } + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("error: wc_InitRng returned %d\n", ret); + goto exit; + } + rngInit = 1; + + ret = wc_SlhDsaKey_Init(&key, param, NULL, INVALID_DEVID); + if (ret != 0) { + printf("error: wc_SlhDsaKey_Init returned %d\n", ret); + goto exit; + } + keyInit = 1; + + sigSz = wc_SlhDsaKey_SigSizeFromParam(param); + if (sigSz <= 0) { + ret = sigSz; + printf("error: wc_SlhDsaKey_SigSizeFromParam returned %d\n", ret); + goto exit; + } + + printf("info: using SLH-DSA-%s: pub %d bytes, priv %d bytes, " + "sig %d bytes\n", paramName, + wc_SlhDsaKey_PublicSizeFromParam(param), + wc_SlhDsaKey_PrivateSizeFromParam(param), sigSz); + + sig = malloc((size_t)sigSz); + if (sig == NULL) { + ret = MEMORY_E; + printf("error: malloc(%d) failed\n", sigSz); + goto exit; + } + sigLen = (word32)sigSz; + + printf("info: making key\n"); + ret = wc_SlhDsaKey_MakeKey(&key, &rng); + if (ret != 0) { + printf("error: wc_SlhDsaKey_MakeKey returned %d\n", ret); + goto exit; + } + + /* ctx=NULL/ctxSz=0 signs with an empty FIPS 205 context string. */ + printf("info: signing message\n"); + ret = wc_SlhDsaKey_Sign(&key, NULL, 0, (const byte*)msg, + (word32)strlen(msg), sig, &sigLen, &rng); + if (ret != 0) { + printf("error: wc_SlhDsaKey_Sign returned %d\n", ret); + goto exit; + } + if (verbose) + dump_hex("signature", sig, sigLen); + + ret = wc_SlhDsaKey_ExportPublic(&key, pub, &pubLen); + if (ret != 0) { + printf("error: wc_SlhDsaKey_ExportPublic returned %d\n", ret); + goto exit; + } + if (verbose) + dump_hex("pub key", pub, pubLen); + + /* --- Verify with public key --- */ + { + SlhDsaKey pubKey; + ret = wc_SlhDsaKey_Init(&pubKey, param, NULL, INVALID_DEVID); + if (ret != 0) goto exit; + /* - only have pub key - */ + wc_SlhDsaKey_ImportPublic(&pubKey, pub, pubLen); + ret = wc_SlhDsaKey_Verify(&pubKey, NULL, 0, (const byte*)msg, + (word32)strlen(msg), sig, sigLen); + if (ret != 0) { + printf("error: wc_SlhDsaKey_Verify returned %d\n", ret); + wc_SlhDsaKey_Free(&pubKey); + goto exit; + } + printf("info: verify message good\n"); + + /* A modified message must fail verification. */ + sig[0] ^= 0x80; + ret = wc_SlhDsaKey_Verify(&pubKey, NULL, 0, (const byte*)msg, + (word32)strlen(msg), sig, sigLen); + if (ret == 0) { + printf("error: verify of corrupted signature succeeded\n"); + ret = -1; + wc_SlhDsaKey_Free(&pubKey); + goto exit; + } + + wc_SlhDsaKey_Free(&pubKey); + } + printf("info: corrupted signature rejected as expected\n"); + + ret = 0; + printf("info: done\n"); + +exit: + free(sig); + if (keyInit) + wc_SlhDsaKey_Free(&key); + if (rngInit) + wc_FreeRng(&rng); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else + +int main(void) +{ + printf("Please build wolfSSL with ./configure --enable-slhdsa " + "(or --enable-slhdsa=yes,sha2 for the SHA2 parameter sets)\n"); + return EXIT_SUCCESS; +} + +#endif /* WOLFSSL_HAVE_SLHDSA */