From dbf6e9ad49689ae3cbcfe2187c098de71fc4524b Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:32 -0400 Subject: [PATCH 01/16] Add SLH-DSA (FIPS 205) example Key generation, signing and verification for all SHAKE and SHA2 parameter sets, with corrupted-signature rejection. --- pq/slh_dsa/Makefile | 23 +++++ pq/slh_dsa/README.md | 44 ++++++++ pq/slh_dsa/slh_dsa.c | 236 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 pq/slh_dsa/Makefile create mode 100644 pq/slh_dsa/README.md create mode 100644 pq/slh_dsa/slh_dsa.c 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..d5c62fd7d --- /dev/null +++ b/pq/slh_dsa/slh_dsa.c @@ -0,0 +1,236 @@ +/* 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; + } + + 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); + + /* 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_Verify(&key, NULL, 0, (const byte*)msg, + (word32)strlen(msg), sig, sigLen); + if (ret != 0) { + printf("error: wc_SlhDsaKey_Verify returned %d\n", ret); + goto exit; + } + printf("info: verify message good\n"); + + /* A modified message must fail verification. */ + sig[0] ^= 0x80; + ret = wc_SlhDsaKey_Verify(&key, NULL, 0, (const byte*)msg, + (word32)strlen(msg), sig, sigLen); + if (ret == 0) { + printf("error: verify of corrupted signature succeeded\n"); + ret = -1; + goto exit; + } + sig[0] ^= 0x80; + 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 */ From eba03900177b67fd78ee2ea82409f59f37ac0a0b Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:39 -0400 Subject: [PATCH 02/16] Add SM2/SM3/SM4 examples wolfCrypt-level ShangMi examples: SM3 hash (GB/T 32905 known answer), SM4-GCM with tamper detection, SM2 sign/verify including the ZA digest step, and SM2 ECDH. Requires the wolfSSL/wolfsm overlay. --- crypto/sm/Makefile | 30 ++++++++ crypto/sm/README.md | 43 +++++++++++ crypto/sm/sm2-ecdh.c | 140 +++++++++++++++++++++++++++++++++ crypto/sm/sm2-sign-verify.c | 146 +++++++++++++++++++++++++++++++++++ crypto/sm/sm3-hash.c | 97 +++++++++++++++++++++++ crypto/sm/sm4-gcm-encrypt.c | 150 ++++++++++++++++++++++++++++++++++++ 6 files changed, 606 insertions(+) create mode 100644 crypto/sm/Makefile create mode 100644 crypto/sm/README.md create mode 100644 crypto/sm/sm2-ecdh.c create mode 100644 crypto/sm/sm2-sign-verify.c create mode 100644 crypto/sm/sm3-hash.c create mode 100644 crypto/sm/sm4-gcm-encrypt.c 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..3a9f730cb --- /dev/null +++ b/crypto/sm/sm2-ecdh.c @@ -0,0 +1,140 @@ +/* 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. */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef WOLFSSL_SM2 + +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; + ecc_key alice; + ecc_key bob; + int aliceInit = 0; + int bobInit = 0; + byte secretA[32]; + byte secretB[32]; + word32 secretASz = (word32)sizeof(secretA); + word32 secretBSz = (word32)sizeof(secretB); + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + + ret = wc_ecc_init(&alice); + if (ret != 0) + goto exit; + aliceInit = 1; + ret = wc_ecc_init(&bob); + if (ret != 0) + goto exit; + bobInit = 1; + + ret = wc_ecc_sm2_make_key(&rng, &alice, WC_ECC_FLAG_NONE); + if (ret == 0) + ret = wc_ecc_sm2_make_key(&rng, &bob, 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"); + +#ifdef ECC_TIMING_RESISTANT + /* Timing-resistant point math needs an RNG on the private key. */ + ret = wc_ecc_set_rng(&alice, &rng); + if (ret == 0) + ret = wc_ecc_set_rng(&bob, &rng); + if (ret != 0) { + printf("wc_ecc_set_rng failed %d\n", ret); + goto exit; + } +#endif + + /* Each side uses its own private key and the peer's public key. */ + ret = wc_ecc_sm2_shared_secret(&alice, &bob, secretA, &secretASz); + if (ret == 0) + ret = wc_ecc_sm2_shared_secret(&bob, &alice, secretB, &secretBSz); + if (ret != 0) { + printf("wc_ecc_sm2_shared_secret failed %d\n", ret); + goto exit; + } + + print_hex("alice secret", secretA, secretASz); + print_hex("bob secret", secretB, secretBSz); + + if (secretASz != secretBSz || + memcmp(secretA, secretB, secretASz) != 0) { + printf("Shared secrets differ!\n"); + ret = -1; + goto exit; + } + printf("Shared secrets match\n"); + ret = 0; + +exit: + if (bobInit) + wc_ecc_free(&bob); + if (aliceInit) + wc_ecc_free(&alice); + 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 */ From 73dde4a27715b37ffe7169b2d66c31568458f68c Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:39 -0400 Subject: [PATCH 03/16] Add BLAKE2b/BLAKE2s examples Incremental hashing checked against the RFC 7693 known-answer vectors, plus BLAKE2b's native keyed mode used as a MAC. --- hash/blake2/Makefile | 26 +++++++ hash/blake2/README.md | 34 +++++++++ hash/blake2/blake2-keyed-mac.c | 132 +++++++++++++++++++++++++++++++++ hash/blake2/blake2b-hash.c | 104 ++++++++++++++++++++++++++ hash/blake2/blake2s-hash.c | 99 +++++++++++++++++++++++++ 5 files changed, 395 insertions(+) create mode 100644 hash/blake2/Makefile create mode 100644 hash/blake2/README.md create mode 100644 hash/blake2/blake2-keyed-mac.c create mode 100644 hash/blake2/blake2b-hash.c create mode 100644 hash/blake2/blake2s-hash.c 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 */ From 9f751293e38861614c5389ad6218cbcb4e6b1c8a Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:39 -0400 Subject: [PATCH 04/16] Add SipHash example SipHash-2-4 one-shot 64/128-bit MACs and the incremental API, checked against the reference implementation's test vector. --- crypto/siphash/Makefile | 16 +++++ crypto/siphash/README.md | 24 +++++++ crypto/siphash/siphash-mac.c | 119 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 crypto/siphash/Makefile create mode 100644 crypto/siphash/README.md create mode 100644 crypto/siphash/siphash-mac.c 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 */ From f09fb7378faa53eff642f25a67e3ed5cf6c14e28 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:39 -0400 Subject: [PATCH 05/16] Add KDF examples HKDF (RFC 5869), PBKDF2 (RFC 2898) and scrypt (RFC 7914), each verified against its RFC known-answer test vector. --- crypto/kdf/Makefile | 26 ++++++++ crypto/kdf/README.md | 34 +++++++++++ crypto/kdf/hkdf.c | 130 ++++++++++++++++++++++++++++++++++++++++ crypto/kdf/pbkdf2.c | 117 ++++++++++++++++++++++++++++++++++++ crypto/kdf/scrypt-kdf.c | 89 +++++++++++++++++++++++++++ 5 files changed, 396 insertions(+) create mode 100644 crypto/kdf/Makefile create mode 100644 crypto/kdf/README.md create mode 100644 crypto/kdf/hkdf.c create mode 100644 crypto/kdf/pbkdf2.c create mode 100644 crypto/kdf/scrypt-kdf.c 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 */ From 96fb285077342e80fcaa97756c1e5cb136f644d8 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:48 -0400 Subject: [PATCH 06/16] Add HPKE seal/open context example One KEM encapsulation protecting an ordered message sequence via wc_HpkeInitSealContext()/wc_HpkeContextSealBase() and the open equivalents, with out-of-order rejection. --- pk/hpke/Makefile | 12 ++- pk/hpke/README.md | 17 ++++- pk/hpke/hpke_context.c | 170 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 pk/hpke/hpke_context.c 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..d8105c3c0 --- /dev/null +++ b/pk/hpke/hpke_context.c @@ -0,0 +1,170 @@ +/* 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 sequence of messages. + * + * wc_HpkeSealBase() does a fresh key encapsulation per message. With a + * seal/open context the encapsulation happens once and each message gets the + * next AEAD nonce, so both sides must process messages in the same order. */ + +#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 +#define MAX_MSG 64 + +static const char* msgs[NUM_MSGS] = { + "first message", + "second message", + "third message" +}; + +int main(void) +{ + int ret; + int i; + Hpke hpke[1]; + HpkeBaseContext sealCtx[1]; + HpkeBaseContext openCtx[1]; + WC_RNG rng[1]; + int rngInit = 0; + void* receiverKey = NULL; + void* ephemeralKey = NULL; + byte pubKey[HPKE_Npk_MAX]; + word16 pubKeySz = (word16)sizeof(pubKey); + byte cipher[NUM_MSGS][MAX_MSG + TAG_SZ]; + byte plain[MAX_MSG]; + const char* info = "hpke context example"; + const char* aad = "message aad"; + +#if defined(HAVE_CURVE25519) + ret = wc_HpkeInit(hpke, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#else + ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); +#endif + if (ret != 0) + goto exit; + + ret = wc_InitRng(rng); + if (ret != 0) + goto exit; + rngInit = 1; + + ret = wc_HpkeGenerateKeyPair(hpke, &receiverKey, rng); + if (ret == 0) + ret = wc_HpkeGenerateKeyPair(hpke, &ephemeralKey, rng); + if (ret != 0) + goto exit; + + /* Sender: one encapsulation, then seal each message in order. */ + ret = wc_HpkeInitSealContext(hpke, sealCtx, ephemeralKey, receiverKey, + (byte*)info, (word32)strlen(info)); + if (ret != 0) + goto exit; + + for (i = 0; i < NUM_MSGS; i++) { + ret = wc_HpkeContextSealBase(hpke, sealCtx, (byte*)aad, + (word32)strlen(aad), (byte*)msgs[i], + (word32)strlen(msgs[i]), cipher[i]); + if (ret != 0) + goto exit; + printf("sealed message %d (%zu bytes)\n", i, strlen(msgs[i])); + } + + /* Only the ephemeral public key travels to the receiver. */ + ret = wc_HpkeSerializePublicKey(hpke, ephemeralKey, pubKey, &pubKeySz); + if (ret != 0) + goto exit; + + /* Receiver: decapsulate once, then open in the same order. */ + ret = wc_HpkeInitOpenContext(hpke, openCtx, receiverKey, pubKey, pubKeySz, + (byte*)info, (word32)strlen(info)); + if (ret != 0) + goto exit; + + for (i = 0; i < NUM_MSGS; i++) { + word32 msgSz = (word32)strlen(msgs[i]); + + memset(plain, 0, sizeof(plain)); + ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad, + (word32)strlen(aad), cipher[i], msgSz, + plain); + if (ret != 0) + goto exit; + if (memcmp(plain, msgs[i], msgSz) != 0) { + printf("message %d mismatch\n", i); + ret = -1; + goto exit; + } + printf("opened message %d: %.*s\n", i, (int)msgSz, plain); + } + + /* Replaying a message out of sequence uses the wrong nonce and fails. */ + ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad, + (word32)strlen(aad), cipher[0], + (word32)strlen(msgs[0]), 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; + + printf("HPKE context test success\n"); + +exit: + if (ret != 0) + printf("HPKE context test error %d: %s\n", ret, + wc_GetErrorString(ret)); + if (ephemeralKey != NULL) + wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, NULL); + if (receiverKey != NULL) + wc_HpkeFreeKey(hpke, hpke->kem, receiverKey, 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 From 6398f4552fac0d451ab265f039a5fe96cb8882c5 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:48 -0400 Subject: [PATCH 07/16] Add SRP-6a SHA-256 example Full exchange (enrollment through mutual proof verification) using SHA-256 and the RFC 5054 2048-bit group. --- pk/srp/Makefile | 13 ++- pk/srp/README.md | 7 ++ pk/srp/srp_params_2048.h | 55 +++++++++++ pk/srp/srp_sha256.c | 202 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 pk/srp/srp_params_2048.h create mode 100644 pk/srp/srp_sha256.c 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..ee590e796 --- /dev/null +++ b/pk/srp/srp_sha256.c @@ -0,0 +1,202 @@ +/* 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; + Srp cli; + Srp srv; + int cliInit = 0; + int srvInit = 0; + byte salt[SALT_SZ]; + byte verifier[KEY_BUF_SZ]; + word32 verifierSz = (word32)sizeof(verifier); + byte clientPub[KEY_BUF_SZ]; + word32 clientPubSz = (word32)sizeof(clientPub); + byte serverPub[KEY_BUF_SZ]; + word32 serverPubSz = (word32)sizeof(serverPub); + byte proof[PROOF_SZ]; + word32 proofSz = (word32)sizeof(proof); + const char* username = "alice"; + const char* password = "password123"; + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + + /* 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) + goto exit; + + ret = wc_SrpInit(&cli, SRP_TYPE_SHA256, SRP_CLIENT_SIDE); + if (ret != 0) + goto exit; + cliInit = 1; + + ret = wc_SrpSetUsername(&cli, (const byte*)username, + (word32)strlen(username)); + if (ret == 0) + ret = wc_SrpSetParams(&cli, srp_n_2048, sizeof(srp_n_2048), + srp_g_2048, sizeof(srp_g_2048), salt, + sizeof(salt)); + if (ret == 0) + ret = wc_SrpSetPassword(&cli, (const byte*)password, + (word32)strlen(password)); + if (ret == 0) + ret = wc_SrpGetVerifier(&cli, verifier, &verifierSz); + if (ret != 0) { + printf("verifier generation failed %d\n", ret); + goto exit; + } + printf("Enrolled user '%s' (verifier %u bytes)\n", username, verifierSz); + + /* Login: client computes its public key A. The enrollment object is + * reused; a real client would build a fresh one the same way. */ + ret = wc_SrpGetPublic(&cli, clientPub, &clientPubSz); + if (ret != 0) { + printf("client wc_SrpGetPublic failed %d\n", ret); + goto exit; + } + + /* Server loads the stored verifier and computes its public key B. */ + ret = wc_SrpInit(&srv, SRP_TYPE_SHA256, SRP_SERVER_SIDE); + if (ret != 0) + goto exit; + srvInit = 1; + + ret = wc_SrpSetUsername(&srv, (const byte*)username, + (word32)strlen(username)); + if (ret == 0) + ret = wc_SrpSetParams(&srv, srp_n_2048, sizeof(srp_n_2048), + srp_g_2048, sizeof(srp_g_2048), salt, + sizeof(salt)); + if (ret == 0) + ret = wc_SrpSetVerifier(&srv, verifier, verifierSz); + if (ret == 0) + ret = wc_SrpGetPublic(&srv, serverPub, &serverPubSz); + if (ret != 0) { + printf("server setup failed %d\n", ret); + goto exit; + } + + /* Both sides derive the session key from the two public keys. */ + ret = wc_SrpComputeKey(&cli, clientPub, clientPubSz, serverPub, + serverPubSz); + if (ret == 0) + ret = wc_SrpComputeKey(&srv, clientPub, clientPubSz, serverPub, + serverPubSz); + if (ret != 0) { + printf("wc_SrpComputeKey failed %d\n", ret); + goto exit; + } + + /* Client proves first; only then does the server prove back. */ + ret = wc_SrpGetProof(&cli, proof, &proofSz); + if (ret == 0) + ret = wc_SrpVerifyPeersProof(&srv, proof, proofSz); + if (ret != 0) { + printf("server rejected client proof %d\n", ret); + goto exit; + } + printf("Server verified client proof\n"); + + proofSz = (word32)sizeof(proof); + ret = wc_SrpGetProof(&srv, proof, &proofSz); + if (ret == 0) + ret = wc_SrpVerifyPeersProof(&cli, proof, proofSz); + if (ret != 0) { + printf("client rejected server proof %d\n", ret); + goto exit; + } + printf("Client verified server proof\n"); + + if (cli.keySz != srv.keySz || + memcmp(cli.key, srv.key, cli.keySz) != 0) { + printf("Session keys differ!\n"); + ret = -1; + goto exit; + } + print_hex("session key", cli.key, cli.keySz); + printf("Session keys match\n"); + ret = 0; + +exit: + if (srvInit) + wc_SrpTerm(&srv); + if (cliInit) + wc_SrpTerm(&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 */ From 0aeba02dce173f50c143137f5d1b19e50d08762f Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:48 -0400 Subject: [PATCH 08/16] Add MIKEY-SAKKE (ECCSI + SAKKE) example Identity-based key exchange per RFC 6507-6509: KMS provisions user key material, ECCSI signs the SAKKE-encapsulated SSV, receiver verifies and derives the shared secret. --- pk/mikey-sakke/Makefile | 16 ++ pk/mikey-sakke/README.md | 44 ++++++ pk/mikey-sakke/mikey-sakke.c | 293 +++++++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 pk/mikey-sakke/Makefile create mode 100644 pk/mikey-sakke/README.md create mode 100644 pk/mikey-sakke/mikey-sakke.c 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..8b4b5da2a --- /dev/null +++ b/pk/mikey-sakke/mikey-sakke.c @@ -0,0 +1,293 @@ +/* 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 PUB_KEY_SZ (32 * 2) + +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; + EccsiKey kmsEccsi; /* KMS master signing key */ + EccsiKey userEccsi; /* Alice/Bob view: KMS public key only */ + int kmsEccsiInit = 0; + int userEccsiInit = 0; + SakkeKey kmsSakke; /* KMS master encryption key */ + SakkeKey userSakke; /* Alice/Bob view: KMS public key only */ + int kmsSakkeInit = 0; + int userSakkeInit = 0; + mp_int ssk; + int sskInit = 0; + ecc_point* pvt = NULL; + ecc_point* rsk = NULL; + byte kpak[PUB_KEY_SZ]; /* KMS ECCSI public key */ + word32 kpakSz = (word32)sizeof(kpak); + byte zpub[PUB_KEY_SZ * 4 + 1]; /* KMS SAKKE public key */ + word32 zpubSz = (word32)sizeof(zpub); + byte hashId[WC_MAX_DIGEST_SIZE]; + byte hashIdSz = 0; + byte ssv[SSV_SZ]; /* plaintext SSV (Alice's copy) */ + word16 ssvSz = SSV_SZ; + byte payload[SSV_SZ + AUTH_SZ]; /* encapsulated SSV || auth */ + word16 authSz = 0; + byte sig[ECCSI_SIG_SZ]; + word32 sigSz = (word32)sizeof(sig); + int verified = 0; + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("wc_InitRng failed %d\n", ret); + goto exit; + } + rngInit = 1; + + /* --- KMS setup: master keys and per-user provisioning. --- */ + ret = wc_InitEccsiKey(&kmsEccsi, NULL, INVALID_DEVID); + if (ret != 0) + goto exit; + kmsEccsiInit = 1; + ret = wc_MakeEccsiKey(&kmsEccsi, &rng); + if (ret != 0) { + printf("wc_MakeEccsiKey failed %d\n", ret); + goto exit; + } + + ret = wc_InitSakkeKey(&kmsSakke, NULL, INVALID_DEVID); + if (ret != 0) + goto exit; + kmsSakkeInit = 1; + ret = wc_MakeSakkeKey(&kmsSakke, &rng); + if (ret != 0) { + printf("wc_MakeSakkeKey failed %d\n", ret); + goto exit; + } + printf("KMS: master ECCSI and SAKKE keys made\n"); + + /* Alice's ECCSI signing pair for her identity. */ + ret = mp_init(&ssk); + if (ret != 0) + goto exit; + sskInit = 1; + pvt = wc_ecc_new_point(); + rsk = wc_ecc_new_point(); + if (pvt == NULL || rsk == NULL) { + ret = MEMORY_E; + goto exit; + } + ret = wc_MakeEccsiPair(&kmsEccsi, &rng, WC_HASH_TYPE_SHA256, aliceId, + (word32)sizeof(aliceId) - 1, &ssk, pvt); + if (ret != 0) { + printf("wc_MakeEccsiPair failed %d\n", ret); + goto exit; + } + printf("KMS: provisioned ECCSI pair for '%s'\n", (const char*)aliceId); + + /* Bob's SAKKE RSK for his identity. */ + ret = wc_MakeSakkeRsk(&kmsSakke, bobId, (word16)sizeof(bobId) - 1, rsk); + if (ret != 0) { + printf("wc_MakeSakkeRsk failed %d\n", ret); + goto exit; + } + printf("KMS: provisioned SAKKE RSK for '%s'\n", (const char*)bobId); + + /* Users get only the KMS public keys. */ + ret = wc_ExportEccsiPublicKey(&kmsEccsi, kpak, &kpakSz, 1); + if (ret == 0) + ret = wc_ExportSakkePublicKey(&kmsSakke, zpub, &zpubSz, 1); + if (ret != 0) { + printf("KMS public key export failed %d\n", ret); + goto exit; + } + + ret = wc_InitEccsiKey(&userEccsi, NULL, INVALID_DEVID); + if (ret != 0) + goto exit; + userEccsiInit = 1; + ret = wc_ImportEccsiPublicKey(&userEccsi, kpak, kpakSz, 1); + if (ret != 0) { + printf("wc_ImportEccsiPublicKey failed %d\n", ret); + goto exit; + } + + ret = wc_InitSakkeKey(&userSakke, NULL, INVALID_DEVID); + if (ret != 0) + goto exit; + userSakkeInit = 1; + ret = wc_ImportSakkePublicKey(&userSakke, zpub, zpubSz, 1); + if (ret != 0) { + printf("wc_ImportSakkePublicKey failed %d\n", ret); + goto exit; + } + + /* --- Alice: encapsulate a fresh SSV to Bob's identity. --- */ + ret = wc_SetSakkeIdentity(&userSakke, bobId, (word16)sizeof(bobId) - 1); + if (ret != 0) + goto exit; + authSz = AUTH_SZ; + ret = wc_GenerateSakkeSSV(&userSakke, &rng, ssv, &ssvSz); + if (ret != 0) { + printf("wc_GenerateSakkeSSV failed %d\n", ret); + goto exit; + } + print_hex("Alice: SSV", ssv, SSV_SZ); + + /* Encapsulation masks the SSV in place; keep Alice's plaintext copy. */ + memcpy(payload, ssv, SSV_SZ); + ret = wc_MakeSakkeEncapsulatedSSV(&userSakke, WC_HASH_TYPE_SHA256, + payload, SSV_SZ, payload + SSV_SZ, + &authSz); + if (ret != 0) { + printf("wc_MakeSakkeEncapsulatedSSV failed %d\n", ret); + goto exit; + } + printf("Alice: SSV encapsulated to '%s' (%u byte auth)\n", + (const char*)bobId, authSz); + + /* Alice signs the whole payload with her identity's ECCSI pair. */ + ret = wc_HashEccsiId(&userEccsi, WC_HASH_TYPE_SHA256, aliceId, + (word32)sizeof(aliceId) - 1, pvt, hashId, &hashIdSz); + if (ret == 0) + ret = wc_SetEccsiHash(&userEccsi, hashId, hashIdSz); + if (ret == 0) + ret = wc_SetEccsiPair(&userEccsi, &ssk, pvt); + if (ret == 0) + ret = wc_SignEccsiHash(&userEccsi, &rng, WC_HASH_TYPE_SHA256, + payload, SSV_SZ + authSz, sig, &sigSz); + if (ret != 0) { + printf("ECCSI signing failed %d\n", ret); + goto exit; + } + printf("Alice: payload signed with ECCSI (%u byte signature)\n", sigSz); + + /* --- Bob: verify Alice's signature, then derive the SSV. --- */ + ret = wc_HashEccsiId(&userEccsi, WC_HASH_TYPE_SHA256, aliceId, + (word32)sizeof(aliceId) - 1, pvt, hashId, &hashIdSz); + if (ret == 0) + ret = wc_SetEccsiHash(&userEccsi, hashId, hashIdSz); + if (ret == 0) + ret = wc_VerifyEccsiHash(&userEccsi, WC_HASH_TYPE_SHA256, payload, + SSV_SZ + authSz, sig, sigSz, &verified); + if (ret != 0 || !verified) { + printf("ECCSI verify failed: ret %d verified %d\n", ret, verified); + ret = -1; + goto exit; + } + printf("Bob: ECCSI signature from '%s' verified\n", + (const char*)aliceId); + + /* Derivation unmasks the SSV in place and validates it against auth. */ + ret = wc_SetSakkeRsk(&userSakke, rsk, NULL, 0); + if (ret == 0) + ret = wc_DeriveSakkeSSV(&userSakke, WC_HASH_TYPE_SHA256, payload, + SSV_SZ, payload + SSV_SZ, authSz); + if (ret != 0) { + printf("wc_DeriveSakkeSSV failed %d\n", ret); + goto exit; + } + print_hex("Bob: SSV", payload, SSV_SZ); + + if (memcmp(ssv, payload, SSV_SZ) != 0) { + printf("SSVs differ!\n"); + ret = -1; + goto exit; + } + printf("Shared Secret Values match\n"); + ret = 0; + +exit: + if (ret != 0) + printf("error %d: %s\n", ret, wc_GetErrorString(ret)); + if (rsk != NULL) + wc_ecc_forcezero_point(rsk); + if (pvt != NULL) + wc_ecc_del_point(pvt); + if (rsk != NULL) + wc_ecc_del_point(rsk); + if (sskInit) + mp_forcezero(&ssk); + if (userSakkeInit) + wc_FreeSakkeKey(&userSakke); + if (kmsSakkeInit) + wc_FreeSakkeKey(&kmsSakke); + if (userEccsiInit) + wc_FreeEccsiKey(&userEccsi); + if (kmsEccsiInit) + wc_FreeEccsiKey(&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 */ From fa32abf94858b13a3602dd8c32467c222175c57c Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 24 Jul 2026 15:18:48 -0400 Subject: [PATCH 09/16] Register new algorithm examples in CI manifest Add check entries for slh_dsa, blake2, siphash, kdf and mikey-sakke; extend the crypto, pq and pk profiles with their configure flags. crypto/sm is a documented skip: it needs the wolfsm overlay patched into the wolfSSL tree before configure. --- .github/examples-manifest.yml | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) 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 From a6933bdb24f8f73bb28a7d0129869f59614f81ae Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 10:14:32 -0600 Subject: [PATCH 10/16] Mikey-Sakke example revised --- pk/mikey-sakke/mikey-sakke.c | 504 ++++++++++++++++++++++++----------- 1 file changed, 341 insertions(+), 163 deletions(-) diff --git a/pk/mikey-sakke/mikey-sakke.c b/pk/mikey-sakke/mikey-sakke.c index 8b4b5da2a..7e5fbee04 100644 --- a/pk/mikey-sakke/mikey-sakke.c +++ b/pk/mikey-sakke/mikey-sakke.c @@ -50,7 +50,9 @@ #define SSV_SZ 16 #define AUTH_SZ 257 #define ECCSI_SIG_SZ 129 -#define PUB_KEY_SZ (32 * 2) +#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"; @@ -67,214 +69,390 @@ static void print_hex(const char* label, const byte* data, word32 len) int main(void) { + int ret; + WC_RNG rng; int rngInit = 0; - EccsiKey kmsEccsi; /* KMS master signing key */ - EccsiKey userEccsi; /* Alice/Bob view: KMS public key only */ - int kmsEccsiInit = 0; - int userEccsiInit = 0; - SakkeKey kmsSakke; /* KMS master encryption key */ - SakkeKey userSakke; /* Alice/Bob view: KMS public key only */ - int kmsSakkeInit = 0; - int userSakkeInit = 0; - mp_int ssk; - int sskInit = 0; - ecc_point* pvt = NULL; - ecc_point* rsk = NULL; - byte kpak[PUB_KEY_SZ]; /* KMS ECCSI public key */ - word32 kpakSz = (word32)sizeof(kpak); - byte zpub[PUB_KEY_SZ * 4 + 1]; /* KMS SAKKE public key */ - word32 zpubSz = (word32)sizeof(zpub); - byte hashId[WC_MAX_DIGEST_SIZE]; - byte hashIdSz = 0; - byte ssv[SSV_SZ]; /* plaintext SSV (Alice's copy) */ - word16 ssvSz = SSV_SZ; - byte payload[SSV_SZ + AUTH_SZ]; /* encapsulated SSV || auth */ - word16 authSz = 0; - byte sig[ECCSI_SIG_SZ]; - word32 sigSz = (word32)sizeof(sig); - int verified = 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 and per-user provisioning. --- */ - ret = wc_InitEccsiKey(&kmsEccsi, NULL, INVALID_DEVID); - if (ret != 0) - goto exit; - kmsEccsiInit = 1; - ret = wc_MakeEccsiKey(&kmsEccsi, &rng); + + /* --- 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 - */ - ret = wc_InitSakkeKey(&kmsSakke, NULL, INVALID_DEVID); - if (ret != 0) - goto exit; - kmsSakkeInit = 1; - ret = wc_MakeSakkeKey(&kmsSakke, &rng); + /* - 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 - */ - /* Alice's ECCSI signing pair for her identity. */ - ret = mp_init(&ssk); - if (ret != 0) - goto exit; - sskInit = 1; - pvt = wc_ecc_new_point(); - rsk = wc_ecc_new_point(); - if (pvt == NULL || rsk == NULL) { - ret = MEMORY_E; - goto exit; - } - ret = wc_MakeEccsiPair(&kmsEccsi, &rng, WC_HASH_TYPE_SHA256, aliceId, - (word32)sizeof(aliceId) - 1, &ssk, pvt); - if (ret != 0) { - printf("wc_MakeEccsiPair failed %d\n", ret); - goto exit; - } - printf("KMS: provisioned ECCSI pair for '%s'\n", (const char*)aliceId); + /* --- KMS setup: master keys --- */ - /* Bob's SAKKE RSK for his identity. */ - ret = wc_MakeSakkeRsk(&kmsSakke, bobId, (word16)sizeof(bobId) - 1, rsk); - if (ret != 0) { - printf("wc_MakeSakkeRsk failed %d\n", ret); - goto exit; - } - printf("KMS: provisioned SAKKE RSK for '%s'\n", (const char*)bobId); - /* Users get only the KMS public keys. */ - ret = wc_ExportEccsiPublicKey(&kmsEccsi, kpak, &kpakSz, 1); + /* --- 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_ExportSakkePublicKey(&kmsSakke, zpub, &zpubSz, 1); - if (ret != 0) { - printf("KMS public key export failed %d\n", ret); - goto exit; - } + 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 - */ - ret = wc_InitEccsiKey(&userEccsi, NULL, INVALID_DEVID); - if (ret != 0) - goto exit; - userEccsiInit = 1; - ret = wc_ImportEccsiPublicKey(&userEccsi, kpak, kpakSz, 1); - if (ret != 0) { - printf("wc_ImportEccsiPublicKey failed %d\n", ret); - goto exit; - } + /* - 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 - */ - ret = wc_InitSakkeKey(&userSakke, NULL, INVALID_DEVID); - if (ret != 0) - goto exit; - userSakkeInit = 1; - ret = wc_ImportSakkePublicKey(&userSakke, zpub, zpubSz, 1); - if (ret != 0) { - printf("wc_ImportSakkePublicKey failed %d\n", ret); - goto exit; - } + /* - 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 - */ - /* --- Alice: encapsulate a fresh SSV to Bob's identity. --- */ - ret = wc_SetSakkeIdentity(&userSakke, bobId, (word16)sizeof(bobId) - 1); - if (ret != 0) - goto exit; - authSz = AUTH_SZ; - ret = wc_GenerateSakkeSSV(&userSakke, &rng, ssv, &ssvSz); - if (ret != 0) { - printf("wc_GenerateSakkeSSV failed %d\n", ret); - goto exit; - } - print_hex("Alice: SSV", ssv, SSV_SZ); + /* --- Enroll Alice with KMS to get their keys --- */ - /* Encapsulation masks the SSV in place; keep Alice's plaintext copy. */ - memcpy(payload, ssv, SSV_SZ); - ret = wc_MakeSakkeEncapsulatedSSV(&userSakke, WC_HASH_TYPE_SHA256, - payload, SSV_SZ, payload + SSV_SZ, - &authSz); - if (ret != 0) { - printf("wc_MakeSakkeEncapsulatedSSV failed %d\n", ret); - goto exit; - } - printf("Alice: SSV encapsulated to '%s' (%u byte auth)\n", - (const char*)bobId, authSz); + /* --- Reset Kms Cert for Bob --- */ + memset(&KmsCertficate, 0, sizeof(KmsCertficate)); + /* --- Reset Kms Cert for Bob --- */ - /* Alice signs the whole payload with her identity's ECCSI pair. */ - ret = wc_HashEccsiId(&userEccsi, WC_HASH_TYPE_SHA256, aliceId, - (word32)sizeof(aliceId) - 1, pvt, hashId, &hashIdSz); - if (ret == 0) - ret = wc_SetEccsiHash(&userEccsi, hashId, hashIdSz); - if (ret == 0) - ret = wc_SetEccsiPair(&userEccsi, &ssk, pvt); - if (ret == 0) - ret = wc_SignEccsiHash(&userEccsi, &rng, WC_HASH_TYPE_SHA256, - payload, SSV_SZ + authSz, sig, &sigSz); - if (ret != 0) { - printf("ECCSI signing failed %d\n", ret); - goto exit; - } - printf("Alice: payload signed with ECCSI (%u byte signature)\n", sigSz); + /* --- Enroll Bob with KMS to get their keys --- */ - /* --- Bob: verify Alice's signature, then derive the SSV. --- */ - ret = wc_HashEccsiId(&userEccsi, WC_HASH_TYPE_SHA256, aliceId, - (word32)sizeof(aliceId) - 1, pvt, hashId, &hashIdSz); - if (ret == 0) - ret = wc_SetEccsiHash(&userEccsi, hashId, hashIdSz); + /* - 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_VerifyEccsiHash(&userEccsi, WC_HASH_TYPE_SHA256, payload, - SSV_SZ + authSz, sig, sigSz, &verified); - if (ret != 0 || !verified) { - printf("ECCSI verify failed: ret %d verified %d\n", ret, verified); - ret = -1; - goto exit; + 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;} } - printf("Bob: ECCSI signature from '%s' verified\n", - (const char*)aliceId); + /* - Message is ready to send - */ + /* --- Alice Creates Message --- */ - /* Derivation unmasks the SSV in place and validates it against auth. */ - ret = wc_SetSakkeRsk(&userSakke, rsk, NULL, 0); - if (ret == 0) - ret = wc_DeriveSakkeSSV(&userSakke, WC_HASH_TYPE_SHA256, payload, - SSV_SZ, payload + SSV_SZ, authSz); - if (ret != 0) { - printf("wc_DeriveSakkeSSV failed %d\n", ret); - goto exit; + /* --- 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); } - print_hex("Bob: SSV", payload, SSV_SZ); - if (memcmp(ssv, payload, SSV_SZ) != 0) { + + 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 (rsk != NULL) - wc_ecc_forcezero_point(rsk); - if (pvt != NULL) - wc_ecc_del_point(pvt); - if (rsk != NULL) - wc_ecc_del_point(rsk); - if (sskInit) - mp_forcezero(&ssk); - if (userSakkeInit) - wc_FreeSakkeKey(&userSakke); - if (kmsSakkeInit) - wc_FreeSakkeKey(&kmsSakke); - if (userEccsiInit) - wc_FreeEccsiKey(&userEccsi); - if (kmsEccsiInit) - wc_FreeEccsiKey(&kmsEccsi); + + 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); From edac0694db455c0df3510d4165bb3d905be7a8fd Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 10:44:24 -0600 Subject: [PATCH 11/16] srp_sha256 revised --- pk/srp/srp_sha256.c | 171 ++++++++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 62 deletions(-) diff --git a/pk/srp/srp_sha256.c b/pk/srp/srp_sha256.c index ee590e796..c54e2f604 100644 --- a/pk/srp/srp_sha256.c +++ b/pk/srp/srp_sha256.c @@ -57,134 +57,181 @@ static void print_hex(const char* label, const byte* data, word32 len) int main(void) { int ret; + WC_RNG rng; int rngInit = 0; - Srp cli; - Srp srv; - int cliInit = 0; - int srvInit = 0; + + /* Shared by both sides: the client picks it at enrollment and the server + * keeps it alongside the verifier. */ byte salt[SALT_SZ]; - byte verifier[KEY_BUF_SZ]; - word32 verifierSz = (word32)sizeof(verifier); - byte clientPub[KEY_BUF_SZ]; - word32 clientPubSz = (word32)sizeof(clientPub); - byte serverPub[KEY_BUF_SZ]; - word32 serverPubSz = (word32)sizeof(serverPub); - byte proof[PROOF_SZ]; - word32 proofSz = (word32)sizeof(proof); - const char* username = "alice"; - const char* password = "password123"; + /* 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. */ + /* --- 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) + if (ret != 0) { + printf("salt generation failed %d\n", ret); goto exit; + } - ret = wc_SrpInit(&cli, SRP_TYPE_SHA256, SRP_CLIENT_SIDE); - if (ret != 0) - goto exit; - cliInit = 1; + ret = wc_SrpInit(&client.cli, SRP_TYPE_SHA256, SRP_CLIENT_SIDE); + if (ret != 0) goto exit; else client.cliInit = 1; - ret = wc_SrpSetUsername(&cli, (const byte*)username, - (word32)strlen(username)); + ret = wc_SrpSetUsername(&client.cli, (const byte*)client.username, + (word32)strlen(client.username)); if (ret == 0) - ret = wc_SrpSetParams(&cli, srp_n_2048, sizeof(srp_n_2048), + 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(&cli, (const byte*)password, - (word32)strlen(password)); - if (ret == 0) - ret = wc_SrpGetVerifier(&cli, verifier, &verifierSz); + 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", username, verifierSz); - - /* Login: client computes its public key A. The enrollment object is - * reused; a real client would build a fresh one the same way. */ - ret = wc_SrpGetPublic(&cli, clientPub, &clientPubSz); + 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(&srv, SRP_TYPE_SHA256, SRP_SERVER_SIDE); - if (ret != 0) - goto exit; - srvInit = 1; + /* --- 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(&srv, (const byte*)username, - (word32)strlen(username)); + ret = wc_SrpSetUsername(&server.srv, (const byte*)server.username, + (word32)strlen(server.username)); if (ret == 0) - ret = wc_SrpSetParams(&srv, srp_n_2048, sizeof(srp_n_2048), + 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(&srv, verifier, verifierSz); - if (ret == 0) - ret = wc_SrpGetPublic(&srv, serverPub, &serverPubSz); + 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(&cli, clientPub, clientPubSz, serverPub, - serverPubSz); + /* --- 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(&srv, clientPub, clientPubSz, serverPub, - serverPubSz); + 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. */ - ret = wc_SrpGetProof(&cli, proof, &proofSz); + /* --- 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(&srv, proof, proofSz); + 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"); - proofSz = (word32)sizeof(proof); - ret = wc_SrpGetProof(&srv, proof, &proofSz); + server.proofSz = (word32)sizeof(server.proof); + ret = wc_SrpGetProof(&server.srv, server.proof, &server.proofSz); if (ret == 0) - ret = wc_SrpVerifyPeersProof(&cli, proof, proofSz); + 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 (cli.keySz != srv.keySz || - memcmp(cli.key, srv.key, cli.keySz) != 0) { + 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", cli.key, cli.keySz); + print_hex("session key", client.cli.key, client.cli.keySz); printf("Session keys match\n"); ret = 0; exit: - if (srvInit) - wc_SrpTerm(&srv); - if (cliInit) - wc_SrpTerm(&cli); + 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); From 6715f3575372a9dd758bba7b0cca1d91adef2f36 Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 11:12:52 -0600 Subject: [PATCH 12/16] hpke_context revised --- pk/hpke/hpke_context.c | 278 +++++++++++++++++++++++++++++------------ 1 file changed, 201 insertions(+), 77 deletions(-) diff --git a/pk/hpke/hpke_context.c b/pk/hpke/hpke_context.c index d8105c3c0..f1cb503c2 100644 --- a/pk/hpke/hpke_context.c +++ b/pk/hpke/hpke_context.c @@ -19,12 +19,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/* Example of HPKE (RFC 9180) context reuse: one KEM encapsulation protecting - * a sequence of messages. +/* Example of HPKE (RFC 9180) context reuse: one KEM encapsulation protecting a + * whole sequence of messages. * - * wc_HpkeSealBase() does a fresh key encapsulation per message. With a - * seal/open context the encapsulation happens once and each message gets the - * next AEAD nonce, so both sides must process messages in the same order. */ + * 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 @@ -39,7 +49,7 @@ defined(HAVE_AESGCM) #define NUM_MSGS 3 -#define TAG_SZ 16 +#define TAG_SZ 16 /* AES-GCM authentication tag */ #define MAX_MSG 64 static const char* msgs[NUM_MSGS] = { @@ -48,92 +58,197 @@ static const char* msgs[NUM_MSGS] = { "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; - Hpke hpke[1]; - HpkeBaseContext sealCtx[1]; - HpkeBaseContext openCtx[1]; - WC_RNG rng[1]; - int rngInit = 0; - void* receiverKey = NULL; - void* ephemeralKey = NULL; - byte pubKey[HPKE_Npk_MAX]; - word16 pubKeySz = (word16)sizeof(pubKey); - byte cipher[NUM_MSGS][MAX_MSG + TAG_SZ]; - byte plain[MAX_MSG]; - const char* info = "hpke context example"; - const char* aad = "message aad"; + 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(hpke, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, - HPKE_AES_128_GCM, NULL); + ret = wc_HpkeInit(&Receiver.suite, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); #else - ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, - HPKE_AES_128_GCM, NULL); + ret = wc_HpkeInit(&Receiver.suite, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, + HPKE_AES_128_GCM, NULL); #endif - if (ret != 0) - goto exit; + if (ret != 0) {printf("Receiver could not init HPKE suite\n"); goto exit;} + /* --- Setup Receiver --- */ - ret = wc_InitRng(rng); - if (ret != 0) + /* --- 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 -- */ - ret = wc_HpkeGenerateKeyPair(hpke, &receiverKey, rng); - if (ret == 0) - ret = wc_HpkeGenerateKeyPair(hpke, &ephemeralKey, rng); - if (ret != 0) - goto exit; + /* --- 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 - */ - /* Sender: one encapsulation, then seal each message in order. */ - ret = wc_HpkeInitSealContext(hpke, sealCtx, ephemeralKey, receiverKey, - (byte*)info, (word32)strlen(info)); - if (ret != 0) - goto exit; + /* - 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 - */ - for (i = 0; i < NUM_MSGS; i++) { - ret = wc_HpkeContextSealBase(hpke, sealCtx, (byte*)aad, - (word32)strlen(aad), (byte*)msgs[i], - (word32)strlen(msgs[i]), cipher[i]); - if (ret != 0) + /* - 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; - printf("sealed message %d (%zu bytes)\n", i, strlen(msgs[i])); + } + /* - Fresh ephemeral pair, one per conversation - */ } + /* --- Sender looks up the Receiver and makes an ephemeral key --- */ - /* Only the ephemeral public key travels to the receiver. */ - ret = wc_HpkeSerializePublicKey(hpke, ephemeralKey, pubKey, &pubKeySz); - if (ret != 0) - goto exit; + /* --- 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 - */ - /* Receiver: decapsulate once, then open in the same order. */ - ret = wc_HpkeInitOpenContext(hpke, openCtx, receiverKey, pubKey, pubKeySz, - (byte*)info, (word32)strlen(info)); - if (ret != 0) - goto exit; + /* - 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 - */ - for (i = 0; i < NUM_MSGS; i++) { - word32 msgSz = (word32)strlen(msgs[i]); + /* - 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 - */ - memset(plain, 0, sizeof(plain)); - ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad, - (word32)strlen(aad), cipher[i], msgSz, - plain); - if (ret != 0) - goto exit; - if (memcmp(plain, msgs[i], msgSz) != 0) { - printf("message %d mismatch\n", i); - ret = -1; - goto exit; + /* - 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); } - printf("opened message %d: %.*s\n", i, (int)msgSz, plain); + /* - Open in the same order the Sender sealed - */ } + /* --- Receiver opens the messages --- */ - /* Replaying a message out of sequence uses the wrong nonce and fails. */ - ret = wc_HpkeContextOpenBase(hpke, openCtx, (byte*)aad, - (word32)strlen(aad), cipher[0], - (word32)strlen(msgs[0]), plain); + /* --- 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; @@ -141,6 +256,7 @@ int main(void) } 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"); @@ -148,12 +264,20 @@ int main(void) if (ret != 0) printf("HPKE context test error %d: %s\n", ret, wc_GetErrorString(ret)); - if (ephemeralKey != NULL) - wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, NULL); - if (receiverKey != NULL) - wc_HpkeFreeKey(hpke, hpke->kem, receiverKey, NULL); + + 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); + wc_FreeRng(&rng); return ret == 0 ? 0 : 1; } @@ -167,4 +291,4 @@ int main(void) return 0; } -#endif +#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */ From 23c020e18655bc6fa4af17c53160a45b95077036 Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 12:37:17 -0600 Subject: [PATCH 13/16] sm2-ecdh revised --- crypto/sm/sm2-ecdh.c | 179 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 38 deletions(-) diff --git a/crypto/sm/sm2-ecdh.c b/crypto/sm/sm2-ecdh.c index 3a9f730cb..2eaa9a684 100644 --- a/crypto/sm/sm2-ecdh.c +++ b/crypto/sm/sm2-ecdh.c @@ -23,7 +23,16 @@ * 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. */ + * 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 @@ -33,9 +42,14 @@ #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; @@ -48,68 +62,148 @@ static void print_hex(const char* label, const byte* data, word32 len) int main(void) { - int ret; - WC_RNG rng; - int rngInit = 0; - ecc_key alice; - ecc_key bob; - int aliceInit = 0; - int bobInit = 0; - byte secretA[32]; - byte secretB[32]; - word32 secretASz = (word32)sizeof(secretA); - word32 secretBSz = (word32)sizeof(secretB); - + 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 -- */ - ret = wc_ecc_init(&alice); - if (ret != 0) - goto exit; - aliceInit = 1; - ret = wc_ecc_init(&bob); - if (ret != 0) - goto exit; - bobInit = 1; - - ret = wc_ecc_sm2_make_key(&rng, &alice, WC_ECC_FLAG_NONE); + /* --- 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, WC_ECC_FLAG_NONE); + 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, &rng); + /* --- 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, &rng); + 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 - /* Each side uses its own private key and the peer's public key. */ - ret = wc_ecc_sm2_shared_secret(&alice, &bob, secretA, &secretASz); + /* --- 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, &alice, secretB, &secretBSz); + 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", secretA, secretASz); - print_hex("bob secret", secretB, secretBSz); + print_hex("alice secret", Alice.sharedSecret, Alice.sharedSecretSz); + print_hex("bob secret", Bob.sharedSecret, Bob.sharedSecretSz); - if (secretASz != secretBSz || - memcmp(secretA, secretB, secretASz) != 0) { + if (Alice.sharedSecretSz != Bob.sharedSecretSz || + memcmp(Alice.sharedSecret, Bob.sharedSecret, + Alice.sharedSecretSz) != 0) { printf("Shared secrets differ!\n"); ret = -1; goto exit; @@ -118,10 +212,19 @@ int main(void) ret = 0; exit: - if (bobInit) - wc_ecc_free(&bob); - if (aliceInit) - wc_ecc_free(&alice); + 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); From 5626aa3a0ba955d59b410466050e1020f4540f08 Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 12:40:40 -0600 Subject: [PATCH 14/16] Mikey-sakke format fix --- pk/mikey-sakke/mikey-sakke.c | 192 +++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 90 deletions(-) diff --git a/pk/mikey-sakke/mikey-sakke.c b/pk/mikey-sakke/mikey-sakke.c index 7e5fbee04..cf28587e6 100644 --- a/pk/mikey-sakke/mikey-sakke.c +++ b/pk/mikey-sakke/mikey-sakke.c @@ -177,68 +177,74 @@ int main(void) /* --- 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: 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; + /* - 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 - */ } - 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); - /* - 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) - */ + if (ret != 0) { + printf("could not export pub eccsi key from KMS"); + goto exit; + } - /* - 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 - */ + 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 --- */ @@ -246,46 +252,52 @@ int main(void) /* --- 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); - /* - 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) - */ + if (ret != 0) { + printf("could not export pub eccsi key from KMS"); + goto exit; + } - /* - 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 - */ + 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 --- */ From 213cb5646f04344134ddc59cd3f5b4648b8b6dae Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 29 Jul 2026 12:59:29 -0600 Subject: [PATCH 15/16] revised slh_dsa --- pq/slh_dsa/slh_dsa.c | 54 ++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/pq/slh_dsa/slh_dsa.c b/pq/slh_dsa/slh_dsa.c index d5c62fd7d..3e581944f 100644 --- a/pq/slh_dsa/slh_dsa.c +++ b/pq/slh_dsa/slh_dsa.c @@ -172,14 +172,6 @@ int main(int argc, char* argv[]) goto exit; } - 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); - /* 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, @@ -191,23 +183,45 @@ int main(int argc, char* argv[]) if (verbose) dump_hex("signature", sig, sigLen); - ret = wc_SlhDsaKey_Verify(&key, NULL, 0, (const byte*)msg, - (word32)strlen(msg), sig, sigLen); + ret = wc_SlhDsaKey_ExportPublic(&key, pub, &pubLen); if (ret != 0) { - printf("error: wc_SlhDsaKey_Verify returned %d\n", ret); + printf("error: wc_SlhDsaKey_ExportPublic returned %d\n", ret); goto exit; } - printf("info: verify message good\n"); + if (verbose) + dump_hex("pub key", pub, pubLen); - /* A modified message must fail verification. */ - sig[0] ^= 0x80; - ret = wc_SlhDsaKey_Verify(&key, NULL, 0, (const byte*)msg, - (word32)strlen(msg), sig, sigLen); - if (ret == 0) { - printf("error: verify of corrupted signature succeeded\n"); - ret = -1; - goto exit; + /* --- 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); } + /* --- Verify with public key --- */ + sig[0] ^= 0x80; printf("info: corrupted signature rejected as expected\n"); From 5c56a1ce911defdad649cfadc09d6d859419adcd Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Thu, 30 Jul 2026 10:49:34 -0600 Subject: [PATCH 16/16] removed useless sig corruption --- pq/slh_dsa/slh_dsa.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/pq/slh_dsa/slh_dsa.c b/pq/slh_dsa/slh_dsa.c index 3e581944f..4fa065be1 100644 --- a/pq/slh_dsa/slh_dsa.c +++ b/pq/slh_dsa/slh_dsa.c @@ -220,9 +220,6 @@ int main(int argc, char* argv[]) wc_SlhDsaKey_Free(&pubKey); } - /* --- Verify with public key --- */ - - sig[0] ^= 0x80; printf("info: corrupted signature rejected as expected\n"); ret = 0;