Purge OQS from wolfSSH. Use kyber from wolfssl.

pull/736/head
Anthony Hu 2024-08-20 14:56:46 -04:00
parent d57f53b852
commit 20287f5568
5 changed files with 132 additions and 143 deletions

View File

@ -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
===================

View File

@ -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/common.h>]], [[ 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/common.h>]], [[ 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"])

View File

@ -54,8 +54,9 @@
#include <wolfssl/wolfcrypt/kdf.h>
#endif
#ifdef WOLFSSH_HAVE_LIBOQS
#include <oqs/kem.h>
#ifdef WOLFSSL_HAVE_KYBER
#include <wolfssl/wolfcrypt/kyber.h>
#include <wolfssl/wolfcrypt/wc_kyber.h>
#endif
#ifdef NO_INLINE
@ -132,8 +133,8 @@ Flags:
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) && (fSz <= (word32)kem->length_ciphertext)) {
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 < 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) &&
(ssh->handshake->eSz <= (word32)kem->length_public_key)) {
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_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) {
*fSz -= kem->length_ciphertext;
ssh->kSz -= kem->length_shared_secret;
ret = wc_KyberKey_Encapsulate(&kem, f, ssh->k, ssh->rng);
}
if (ret == 0) {
*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 (kem != NULL) {
OQS_KEM_free(kem);
if (ret == 0) {
ret = wc_KyberKey_PublicKeySize(&kem, &length_publickey);
}
if (ret == 0) {
ret = wc_KyberKey_PrivateKeySize(&kem, &length_privatekey);
}
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) {

View File

@ -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

View File

@ -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