diff --git a/pk/srp/Makefile b/pk/srp/Makefile index b2d0def4..ed12cdf5 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 cc0de5f7..03d6b980 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 00000000..a881a316 --- /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 00000000..ee590e79 --- /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 */