diff --git a/README.md b/README.md index eeee0932f..357643931 100644 --- a/README.md +++ b/README.md @@ -450,33 +450,22 @@ The wolfSSH client and server will automatically negotiate using Curve25519. POST-QUANTUM ============ -wolfSSH now supports the post-quantum algorithm Kyber. It uses the NIST -submission's Level 1 parameter set implemented by liboqs via an integration -with wolfSSH. It is hybridized with ECDHE over the P-256 ECC curve. +wolfSSH now supports the post-quantum algorithm ML-DSA (also known as Kyber). +It uses the KYBER512 parameter set and is hybridized with ECDHE over the P-256 +ECC curve. -In order be able to use liboqs, you must have it built and installed on your -system. We support the 0.7.0 release of liboqs. You can download it from the -following link: +In order to use this key exchange you must build and install wolfSSL on your +system. Here is an example of an effective configuration: - https://github.com/open-quantum-safe/liboqs/archive/refs/tags/0.7.0.tar.gz + $ ./configure --enable-wolfssh --enable-experimental --enable-kyber -Once unpacked, this would be sufficient: +After that, simply configure and build wolfssh as usual: - $ cd liboqs-0.7.0 - $ mkdir build - $ cd build - $ cmake -DOQS_USE_OPENSSL=0 .. + $ ./configure $ make all - $ sudo make install - -In order to enable support for Kyber Level1 hybridized with ECDHE over the P-256 -ECC curve in wolfSSH, use the `--with-liboqs` build option during configuration: - - $ ./configure --with-liboqs - -The wolfSSH client and server will automatically negotiate using Kyber Level1 -hybridized with ECDHE over the P-256 ECC curve if this feature is enabled. +The wolfSSH client and server will automatically negotiate using KYBER512 +hybridized with ECDHE over the P-256 ECC curve. $ ./examples/echoserver/echoserver -f @@ -508,7 +497,6 @@ NOTE: when prompted, enter the password which is "upthehill". You can type a line of text and when you press enter, the line will be echoed back. Use CTRL-C to terminate the connection. - CERTIFICATE SUPPORT =================== diff --git a/configure.ac b/configure.ac index 617ff0523..808f862a7 100644 --- a/configure.ac +++ b/configure.ac @@ -60,46 +60,6 @@ AC_CHECK_HEADERS([limits.h sys/select.h sys/time.h sys/ioctl.h pty.h util.h term AC_CHECK_LIB([network],[socket]) AC_CHECK_LIB([util],[forkpty]) -# liboqs -ENABLED_LIBOQS="no" -tryliboqsdir="" -AC_ARG_WITH([liboqs], - [AS_HELP_STRING([--with-liboqs=PATH],[Path to liboqs install (default /usr/local) EXPERIMENTAL!])], - [ - AC_MSG_CHECKING([for liboqs]) - CPPFLAGS="$CPPFLAGS -DWOLFSSH_HAVE_LIBOQS" - LIBS="$LIBS -loqs" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ OQS_init(); ]])], [ liboqs_linked=yes ],[ liboqs_linked=no ]) - - if test "x$liboqs_linked" = "xno" ; then - if test "x$withval" != "xno" ; then - tryliboqsdir=$withval - fi - if test "x$withval" = "xyes" ; then - tryliboqsdir="/usr/local" - fi - - LDFLAGS="$AM_LDFLAGS $LDFLAGS -L$tryliboqsdir/lib" - CPPFLAGS="$CPPFLAGS -I$tryliboqsdir/include" - - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ OQS_init(); ]])], [ liboqs_linked=yes ],[ liboqs_linked=no ]) - - if test "x$liboqs_linked" = "xno" ; then - AC_MSG_ERROR([liboqs isn't found. - If it's already installed, specify its path using --with-liboqs=/dir/]) - fi - AC_MSG_RESULT([yes]) - AM_LDFLAGS="$AM_LDFLAGS -L$tryliboqsdir/lib" - else - AC_MSG_RESULT([yes]) - fi - - AM_CFLAGS="$AM_CFLAGS -DWOLFSSH_HAVE_LIBOQS" - ENABLED_LIBOQS="yes" - ] -) - #wolfssl AC_MSG_CHECKING([for wolfSSL]) if test "x$prefix" = "xNONE" @@ -365,4 +325,3 @@ AS_ECHO([" * agent: $ENABLED_AGENT"]) AS_ECHO([" * TCP/IP Forwarding: $ENABLED_FWD"]) AS_ECHO([" * X.509 Certs: $ENABLED_CERTS"]) AS_ECHO([" * Examples: $ENABLED_EXAMPLES"]) -AS_ECHO([" * liboqs Integration: $ENABLED_LIBOQS"]) diff --git a/src/internal.c b/src/internal.c index 3e7a978b8..e49437a6b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -54,8 +54,9 @@ #include #endif -#ifdef WOLFSSH_HAVE_LIBOQS -#include +#ifdef WOLFSSL_HAVE_KYBER +#include +#include #endif #ifdef NO_INLINE @@ -132,8 +133,8 @@ Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server authentication with prime NISTP521. WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 - Set when there is no liboqs integration. Set to disable use of ECDHE with - prime NISTP256 hybridized with post-quantum Kyber Level1 KEM. + Set when Kyber is disabled in wolfssl. Set to disable use of ECDHE with + prime NISTP256 hybridized with post-quantum KYBER512 KEM. WOLFSSH_NO_AES_CBC Set when AES or AES-CBC are disabled. Set to disable use of AES-CBC encryption. @@ -2456,7 +2457,6 @@ static const NameIdPair NameIdMap[] = { { ID_ECDH_SHA2_NISTP521, TYPE_KEX, "ecdh-sha2-nistp521" }, #endif #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 - /* We use kyber-512 here to achieve interop with OQS's fork. */ { ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256, TYPE_KEX, "ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org" }, #endif @@ -5052,12 +5052,12 @@ static int KeyAgreeCurve25519_client(WOLFSSH* ssh, byte hashId, #endif /* WOLFSSH_NO_CURVE25519_SHA256 */ -/* KeyAgreeEcdhKyber1_client +/* KeyAgreeEcdhKyber512_client * hashId - wolfCrypt hash type ID used * f - peer public key * fSz - peer public key size */ -static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, +static int KeyAgreeEcdhKyber512_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz) #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 { @@ -5065,7 +5065,10 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, byte sharedSecretHashSz = 0; byte *sharedSecretHash = NULL; ecc_key *key_ptr = NULL; - OQS_KEM *kem; + KyberKey kem = {0}; + word32 length_ciphertext = 0; + word32 length_sharedsecret = 0; + word32 length_privatekey = 0; #ifndef WOLFSSH_SMALL_STACK ecc_key key_s; @@ -5080,17 +5083,33 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, key_ptr = &key_s; #endif /* WOLFSSH_SMALL_STACK */ - WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber1_client()"); + WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber512_client()"); /* This is a a hybrid of ECDHE and a post-quantum KEM. In this * case, I need to generated the ECC shared secret and * decapsulate the ciphertext of the post-quantum KEM. */ - kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); - if (kem == NULL) { - ret = WS_MEMORY_E; + + if (ret == 0) { + ret = wc_KyberKey_Init(KYBER512, &kem, ssh->ctx->heap, INVALID_DEVID); + } + + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize(&kem, &length_ciphertext); + } + + if (ret == 0) { + ret = wc_KyberKey_SharedSecretSize(&kem, &length_sharedsecret); + } + + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(&kem, &length_privatekey); + } + + if ((ret == 0) && (ssh->handshake->xSz < length_privatekey)) { + ret = WS_BUFFER_E; } - if ((ret == 0) && (fSz <= (word32)kem->length_ciphertext)) { + if ((ret == 0) && (fSz < length_ciphertext)) { ret = WS_BUFFER_E; } @@ -5103,16 +5122,15 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, } #endif if (ret == 0) { - ret = wc_ecc_import_x963(f + kem->length_ciphertext, - fSz - (word32)kem->length_ciphertext, - key_ptr); + ret = wc_ecc_import_x963(f + length_ciphertext, fSz - length_ciphertext, + key_ptr); } if (ret == 0) { PRIVATE_KEY_UNLOCK(); ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, - key_ptr, ssh->k + kem->length_shared_secret, - &ssh->kSz); + key_ptr, ssh->k + length_sharedsecret, + &ssh->kSz); PRIVATE_KEY_LOCK(); } wc_ecc_free(key_ptr); @@ -5124,14 +5142,16 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, wc_ecc_free(&ssh->handshake->privKey.ecc); if (ret == 0) { - if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x) - != OQS_SUCCESS) { - ret = WS_ERROR; - } + wc_KyberKey_DecodePrivateKey(&kem, ssh->handshake->x, + length_privatekey); } if (ret == 0) { - ssh->kSz += kem->length_shared_secret; + ret = wc_KyberKey_Decapsulate(&kem, ssh->k, f, length_ciphertext); + } + + if (ret == 0) { + ssh->kSz += length_sharedsecret; } else { ssh->kSz = 0; WLOG(WS_LOG_ERROR, @@ -5139,9 +5159,7 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, ret); } - if (kem != NULL) { - OQS_KEM_free(kem); - } + wc_KyberKey_Free(&kem); /* Replace the concatenated shared secrets with the hash. That * will become the new shared secret. */ @@ -5170,7 +5188,7 @@ static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId, WFREE(sharedSecretHash, ssh->ctx->heap, DYNTYPE_PRIVKEY); } - WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber1_client(), ret = %d", ret); + WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber512_client(), ret = %d", ret); return ret; } #else /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */ @@ -5208,7 +5226,7 @@ static int KeyAgree_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz) ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz); } else if (ssh->handshake->useEccKyber) { - ret = KeyAgreeEcdhKyber1_client(ssh, hashId, f, fSz); + ret = KeyAgreeEcdhKyber512_client(ssh, hashId, f, fSz); } else { ret = WS_INVALID_ALGO_ID; @@ -10903,7 +10921,7 @@ static int KeyAgreeCurve25519_server(WOLFSSH* ssh, byte hashId, #endif /* WOLFSSH_NO_CURVE25519_SHA256 */ -/* KeyAgreeEcdhKyber1_server +/* KeyAgreeEcdhKyber512_server * hashId - wolfCrypt hash type ID used * f - peer public key * fSz - peer public key size @@ -10914,14 +10932,17 @@ static int KeyAgreeCurve25519_server(WOLFSSH* ssh, byte hashId, * generate and encapsulate the shared secret and send the * ciphertext. */ -static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, +static int KeyAgreeEcdhKyber512_server(WOLFSSH* ssh, byte hashId, byte* f, word32* fSz) #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 { int ret = WS_SUCCESS; byte sharedSecretHashSz = 0; byte *sharedSecretHash = NULL; - OQS_KEM* kem = NULL; + KyberKey kem = {0}; + word32 length_publickey = 0; + word32 length_ciphertext = 0; + word32 length_sharedsecret = 0; ecc_key* pubKey = NULL; ecc_key* privKey = NULL; int primeId; @@ -10929,7 +10950,7 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, ecc_key eccKeys[2]; #endif - WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber1_server()"); + WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber512_server()"); #ifdef WOLFSSH_SMALL_STACK pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), @@ -10954,40 +10975,49 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, } if (ret == 0) { - kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); - if (kem == NULL) { - ret = WS_MEMORY_E; - } + ret = wc_KyberKey_Init(KYBER512, &kem, ssh->ctx->heap, + INVALID_DEVID); + } + + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize(&kem, &length_ciphertext); + } + + if (ret == 0) { + ret = wc_KyberKey_SharedSecretSize(&kem, &length_sharedsecret); } - if ((ret == 0) && - (ssh->handshake->eSz <= (word32)kem->length_public_key)) { + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(&kem, &length_publickey); + } + + if ((ret == 0) && (ssh->handshake->eSz <= length_publickey)) { ret = WS_BUFFER_E; } if (ret == 0) { - if (OQS_KEM_encaps(kem, f, ssh->k, ssh->handshake->e) != OQS_SUCCESS) { - ret = WS_PUBKEY_REJECTED_E; - WLOG(WS_LOG_ERROR, - "Generate ECC-kyber (encap) shared secret failed, %d", - ret); - *fSz = 0; - ssh->kSz = 0; - } + ret = wc_KyberKey_DecodePublicKey(&kem, ssh->handshake->e, + length_publickey); + } + + if (ret == 0) { + ret = wc_KyberKey_Encapsulate(&kem, f, ssh->k, ssh->rng); } if (ret == 0) { - *fSz -= kem->length_ciphertext; - ssh->kSz -= kem->length_shared_secret; + *fSz -= length_ciphertext; + ssh->kSz -= length_sharedsecret; } else { + ret = WS_PUBKEY_REJECTED_E; + WLOG(WS_LOG_ERROR, + "Generate ECC-kyber (encap) shared secret failed, %d", ret); *fSz = 0; ssh->kSz = 0; - WLOG(WS_LOG_ERROR, - "Generate ECC-kyber (encap) shared secret failed, %d", - ret); } + wc_KyberKey_Free(&kem); + if (ret == 0) { ret = wc_ecc_init_ex(pubKey, ssh->ctx->heap, INVALID_DEVID); } @@ -11001,8 +11031,8 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, #endif if (ret == 0) { ret = wc_ecc_import_x963_ex( - ssh->handshake->e + kem->length_public_key, - ssh->handshake->eSz - (word32)kem->length_public_key, + ssh->handshake->e + length_publickey, + ssh->handshake->eSz - length_publickey, pubKey, primeId); } if (ret == 0) { @@ -11012,17 +11042,17 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, } if (ret == 0) { PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_export_x963(privKey, f + kem->length_ciphertext, fSz); + ret = wc_ecc_export_x963(privKey, f + length_ciphertext, fSz); PRIVATE_KEY_LOCK(); - *fSz += kem->length_ciphertext; + *fSz += length_ciphertext; } if (ret == 0) { word32 tmp_kSz = ssh->kSz; PRIVATE_KEY_UNLOCK(); ret = wc_ecc_shared_secret(privKey, pubKey, - ssh->k + kem->length_shared_secret, &tmp_kSz); + ssh->k + length_sharedsecret, &tmp_kSz); PRIVATE_KEY_LOCK(); - ssh->kSz = (word32)kem->length_shared_secret + tmp_kSz; + ssh->kSz = length_sharedsecret + tmp_kSz; } wc_ecc_free(privKey); wc_ecc_free(pubKey); @@ -11032,10 +11062,6 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, if (privKey) WFREE(privKey, ssh->ctx->heap, DYNTYPE_PUBKEY); #endif - if (kem != NULL) { - OQS_KEM_free(kem); - kem = NULL; - } /* Replace the concatenated shared secrets with the hash. That * will become the new shared secret.*/ @@ -11061,7 +11087,7 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, WFREE(sharedSecretHash, ssh->ctx->heap, DYNTYPE_PRIVKEY); } - WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber1_server(), ret = %d", ret); + WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber512_server(), ret = %d", ret); return ret; } #else /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */ @@ -11513,7 +11539,7 @@ int SendKexDhReply(WOLFSSH* ssh) ret = KeyAgreeCurve25519_server(ssh, hashId, f_ptr, &fSz); } else if (useEccKyber) { - ret = KeyAgreeEcdhKyber1_server(ssh, hashId, f_ptr, &fSz); + ret = KeyAgreeEcdhKyber512_server(ssh, hashId, f_ptr, &fSz); } } @@ -12126,28 +12152,44 @@ int SendKexDhInit(WOLFSSH* ssh) #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 if (ssh->handshake->useEccKyber) { - OQS_KEM* kem = NULL; + KyberKey kem = {0}; + word32 length_publickey = 0; + word32 length_privatekey = 0; ret = 0; - kem = OQS_KEM_new(OQS_KEM_alg_kyber_512); - if (kem == NULL) { - ret = WS_INVALID_ALGO_ID; + if (ret == 0) { + ret = wc_KyberKey_Init(KYBER512, &kem, ssh->ctx->heap, + INVALID_DEVID); } - /* Move ecc to the back. Note that this assumes the PQ public key - * is bigger than the ECC public key. */ if (ret == 0) { - XMEMCPY(e + kem->length_public_key, e, eSz); - if (OQS_KEM_keypair(kem, e, ssh->handshake->x) != OQS_SUCCESS) { - /* This should never happen */ - ret = WS_ERROR; - } - eSz += kem->length_public_key; - ssh->handshake->xSz = (word32)kem->length_secret_key; + ret = wc_KyberKey_MakeKey(&kem, ssh->rng); + } + + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(&kem, &length_publickey); + } + + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(&kem, &length_privatekey); } - if (kem != NULL) { - OQS_KEM_free(kem); + + if (ret == 0) { + /* Move ecc to the back and put PQ Key in the front. Note that + * this assumes the PQ public key is bigger than the ECC public + * key. */ + XMEMCPY(e + length_publickey, e, eSz); + ret = wc_KyberKey_EncodePublicKey(&kem, e, length_publickey); + eSz += length_publickey; } + + if (ret == 0) { + ret = wc_KyberKey_EncodePrivateKey(&kem, ssh->handshake->x, + length_privatekey); + ssh->handshake->xSz = length_privatekey; + } + + wc_KyberKey_Free(&kem); } #endif /* ! WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */ if (ret == 0) { diff --git a/src/ssh.c b/src/ssh.c index 6e67d0ac3..e25037925 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2994,7 +2994,7 @@ size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str, size_t strSz) #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256: - ret = WSNPRINTF(str, strSz, "%s", "Kyber1"); + ret = WSNPRINTF(str, strSz, "%s", "ECDH-KYBER512"); break; #endif diff --git a/wolfssh/internal.h b/wolfssh/internal.h index c020526e2..7aa36d87b 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -163,7 +163,7 @@ extern "C" { #undef WOLFSSH_NO_ECDH_SHA2_NISTP521 #define WOLFSSH_NO_ECDH_SHA2_NISTP521 #endif -#if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256) \ +#if !defined(WOLFSSL_HAVE_KYBER) || defined(NO_SHA256) \ || defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) #undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 #define WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256