From a2635be9e6d645e8d73da09789401fdcf06cc539 Mon Sep 17 00:00:00 2001 From: Anthony Hu Date: Fri, 9 Sep 2022 11:41:30 -0400 Subject: [PATCH] wolfCrypt support for external Kyber implementations (liboqs and pqm4) --- configure.ac | 134 ++++--- src/include.am | 1 + src/tls.c | 18 +- wolfcrypt/benchmark/benchmark.c | 2 + wolfcrypt/src/ext_kyber.c | 676 ++++++++++++++++++++++++++++++++ wolfcrypt/test/test.c | 7 + wolfssl/wolfcrypt/ext_kyber.h | 62 +++ wolfssl/wolfcrypt/include.am | 3 +- 8 files changed, 832 insertions(+), 71 deletions(-) create mode 100644 wolfcrypt/src/ext_kyber.c create mode 100644 wolfssl/wolfcrypt/ext_kyber.h diff --git a/configure.ac b/configure.ac index 603d5483a..9a841d2a6 100644 --- a/configure.ac +++ b/configure.ac @@ -963,6 +963,53 @@ then fi +# 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 -DHAVE_LIBOQS -DHAVE_TLS_EXTENSIONS" + 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 + + if test "x$ENABLED_OPENSSLEXTRA" = "xno" && test "x$ENABLED_OPENSSLCOEXIST" = "xno" + then + ENABLED_OPENSSLEXTRA="yes" + AM_CFLAGS="$AM_CFLAGS -DOPENSSL_EXTRA" + fi + + AM_CFLAGS="$AM_CFLAGS -DHAVE_LIBOQS -DHAVE_TLS_EXTENSIONS" + ENABLED_LIBOQS="yes" + ] +) + + # KYBER # Used: # - SHA3, Shake128 and Shake256, or @@ -1013,24 +1060,33 @@ done if test "$ENABLED_KYBER" != "no" then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_KYBER" - if test "$ENABLED_KYBER512" = ""; then - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER512" - fi - if test "$ENABLED_KYBER768" = ""; then - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER768" - fi - if test "$ENABLED_KYBER1024" = ""; then - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER1024" - fi + if test "$ENABLED_WC_KYBER" = "yes" + then + if test "$ENABLED_KYBER512" = ""; then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER512" + fi + if test "$ENABLED_KYBER768" = ""; then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER768" + fi + if test "$ENABLED_KYBER1024" = ""; then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_KYBER1024" + fi - if test "$ENABLED_KYBER_90S" != "yes"; then - test "$enable_sha3" = "" && enable_sha3=yes - test "$enable_shake128" = "" && enable_shake128=yes - test "$enable_shake256" = "" && enable_shake256=yes + if test "$ENABLED_KYBER_90S" != "yes"; then + test "$enable_sha3" = "" && enable_sha3=yes + test "$enable_shake128" = "" && enable_shake128=yes + test "$enable_shake256" = "" && enable_shake256=yes + else + test "$enable_sha256" = "" && enable_sha256=yes + test "$enable_sha512" = "" && enable_sha512=yes + test "$enable_aesctr" = "" && enable_aesctr=yes + fi else - test "$enable_sha256" = "" && enable_sha256=yes - test "$enable_sha512" = "" && enable_sha512=yes - test "$enable_aesctr" = "" && enable_aesctr=yes + # Default is to use liboqs. Make sure its enabled. + if test "$ENABLED_LIBOQS" = "no"; then + AC_MSG_ERROR([The default implementation for kyber is liboqs. + Please use --with-liboqs.]) + fi fi fi @@ -4604,52 +4660,6 @@ then AC_MSG_ERROR([cannot enable user crypto and fips, user crypto posibility of using code in fips boundary.]) fi -# 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 -DHAVE_LIBOQS -DHAVE_TLS_EXTENSIONS" - 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 - - if test "x$ENABLED_OPENSSLEXTRA" = "xno" && test "x$ENABLED_OPENSSLCOEXIST" = "xno" - then - ENABLED_OPENSSLEXTRA="yes" - AM_CFLAGS="$AM_CFLAGS -DOPENSSL_EXTRA" - fi - - AM_CFLAGS="$AM_CFLAGS -DHAVE_LIBOQS -DHAVE_TLS_EXTENSIONS" - ENABLED_LIBOQS="yes" - ] -) - # Whitewood netRandom client library ENABLED_WNR="no" trywnrdir="" diff --git a/src/include.am b/src/include.am index 07c776270..fa8a66b07 100644 --- a/src/include.am +++ b/src/include.am @@ -687,6 +687,7 @@ if BUILD_LIBOQS src_libwolfssl_la_SOURCES += wolfcrypt/src/falcon.c src_libwolfssl_la_SOURCES += wolfcrypt/src/dilithium.c src_libwolfssl_la_SOURCES += wolfcrypt/src/sphincs.c +src_libwolfssl_la_SOURCES += wolfcrypt/src/ext_kyber.c endif if BUILD_LIBZ diff --git a/src/tls.c b/src/tls.c index a0034c532..3b8a5a030 100644 --- a/src/tls.c +++ b/src/tls.c @@ -53,15 +53,17 @@ #include #ifdef WOLFSSL_WC_KYBER #include -#endif #elif defined(HAVE_LIBOQS) #include + #include #elif defined(HAVE_PQM4) #include "api_kyber.h" #define PQM4_PUBLIC_KEY_LENGTH CRYPTO_PUBLICKEYBYTES #define PQM4_PRIVATE_KEY_LENGTH CRYPTO_SECRETKEYBYTES #define PQM4_SHARED_SECRET_LENGTH CRYPTO_BYTES #define PQM4_CIPHERTEXT_LENGTH CRYPTO_CIPHERTEXTBYTES + #include +#endif #endif #endif @@ -7058,7 +7060,7 @@ static int TLSX_KeyShare_GenEccKey(WOLFSSL *ssl, KeyShareEntry* kse) } #ifdef HAVE_PQC -#ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER static int kyber_id2type(int id, int *type) { int ret = 0; @@ -7183,7 +7185,7 @@ static void findEccPqc(int *ecc, int *pqc, int group) * kse The key share entry object. * returns 0 on success, otherwise failure. */ -#ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) { int ret = 0; @@ -8084,7 +8086,7 @@ static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) } #ifdef HAVE_PQC -#ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER /* Process the Kyber key share extension on the client side. * * ssl The SSL/TLS object. @@ -8881,7 +8883,7 @@ static int TLSX_KeyShare_New(KeyShareEntry** list, int group, void *heap, } #ifdef HAVE_PQC -#ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER static int server_generate_pqc_ciphertext(WOLFSSL* ssl, KeyShareEntry* keyShareEntry, byte* data, word16 len) { @@ -9534,7 +9536,7 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) #endif #endif #ifdef HAVE_PQC - #ifdef WOLFSSL_HAVE_KYBER + #ifdef WOLFSSL_WC_KYBER #ifdef WOLFSSL_KYBER512 case WOLFSSL_KYBER_LEVEL1: #endif @@ -9651,7 +9653,7 @@ static int TLSX_KeyShare_GroupRank(WOLFSSL* ssl, int group) /* For the liboqs groups we need to do a runtime check because * liboqs could be compiled to make an algorithm unavailable. */ - #ifdef WOLFSSL_HAVE_KYBER + #ifdef WOLFSSL_WC_KYBER #ifdef WOLFSSL_KYBER512 if (TLSX_KeyShare_IsSupported(WOLFSSL_KYBER_LEVEL1)) ssl->group[ssl->numGroups++] = WOLFSSL_KYBER_LEVEL1; @@ -11733,7 +11735,7 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) #endif #ifdef HAVE_PQC -#ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER #ifdef WOLFSSL_KYBER512 if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_KYBER_LEVEL1, diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 063a31a0a..fb3930072 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -269,6 +269,7 @@ #ifdef HAVE_LIBOQS #include #include + #include #endif #if defined(HAVE_PQC) @@ -290,6 +291,7 @@ #define PQM4_SHARED_SECRET_LENGTH CRYPTO_BYTES #define PQM4_CIPHERTEXT_LENGTH CRYPTO_CIPHERTEXTBYTES typedef char OQS_KEM; + #include #endif #include diff --git a/wolfcrypt/src/ext_kyber.c b/wolfcrypt/src/ext_kyber.c new file mode 100644 index 000000000..996b37054 --- /dev/null +++ b/wolfcrypt/src/ext_kyber.c @@ -0,0 +1,676 @@ +/* ext_kyber.c + * + * Copyright (C) 2006-2022 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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-1335, USA + */ + +#include +#include + +#if defined(WOLFSSL_KYBER_90S) && defined(HAVE_PQM4) +#error "KYBER-90s is not supported when building PQM4" +#endif + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#if defined (HAVE_LIBOQS) +static const char* OQS_ID2name(int id) { + switch (id) { +#ifdef WOLFSSL_KYBER_90S + case KYBER_LEVEL1: return OQS_KEM_alg_kyber_512_90s; + case KYBER_LEVEL3: return OQS_KEM_alg_kyber_768_90s; + case KYBER_LEVEL5: return OQS_KEM_alg_kyber_1024_90s; +#else + case KYBER_LEVEL1: return OQS_KEM_alg_kyber_512; + case KYBER_LEVEL3: return OQS_KEM_alg_kyber_768; + case KYBER_LEVEL5: return OQS_KEM_alg_kyber_1024; +#endif + default: break; + } + return NULL; +} +#endif + +/******************************************************************************/ +/* Initializer and cleanup functions. */ + +/** + * Initialize the Kyber key. + * + * @param [in] type Type of key: KYBER512, KYBER768, KYBER1024. + * @param [out] key Kyber key object to initialize. + * @param [in] heap Dynamic memory hint. + * @param [in] devId Device Id. + * @return 0 on success. + * @return BAD_FUNC_ARG when key is NULL or type is unrecognized. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_Init(int type, KyberKey* key, void* heap, int devId) +{ + int ret = 0; + + /* Validate key. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + /* Validate type. */ + switch (type) { + case KYBER_LEVEL1: +#ifdef HAVE_LIBOQS + case KYBER_LEVEL3: + case KYBER_LEVEL5: +#endif /* HAVE_LIBOQS */ + break; + default: + /* No other values supported. */ + ret = BAD_FUNC_ARG; + break; + } + } + if (ret == 0) { + /* Zero out all data. */ + XMEMSET(key, 0, sizeof(*key)); + + /* Keep type for parameters. */ + key->type = type; + } + + (void)devId; + (void)heap; + + return ret; +} + +/** + * Free the Kyber key object. + * + * @param [in, out] key Kyber key object to dispose of. + */ +void wc_KyberKey_Free(KyberKey* key) +{ + if (key != NULL) { + /* Ensure all private data is zeroed. */ + ForceZero(key, sizeof(*key)); + } +} + +/******************************************************************************/ +/* Data size getters. */ + +/** + * Get the size in bytes of encoded private key for the key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of encoded private key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_PrivateKeySize(KyberKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + +#ifdef HAVE_LIBOQS + /* NOTE: SHAKE and AES variants have the same length private key. */ + if (ret == 0) { + switch (key->type) { + case KYBER_LEVEL1: + *len = OQS_KEM_kyber_512_length_secret_key; + break; + case KYBER_LEVEL3: + *len = OQS_KEM_kyber_768_length_secret_key; + break; + case KYBER_LEVEL5: + *len = OQS_KEM_kyber_1024_length_secret_key; + break; + default: + /* No other values supported. */ + ret = BAD_FUNC_ARG; + break; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + (void)key; + if (ret == 0) { + *len = PQM4_PRIVATE_KEY_LENGTH; + } +#endif /* HAVE_PQM4 */ + + return ret; +} + +/** + * Get the size in bytes of encoded public key for the key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of encoded public key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_PublicKeySize(KyberKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + +#ifdef HAVE_LIBOQS + /* NOTE: SHAKE and AES variants have the same length public key. */ + if (ret == 0) { + switch (key->type) { + case KYBER_LEVEL1: + *len = OQS_KEM_kyber_512_length_public_key; + break; + case KYBER_LEVEL3: + *len = OQS_KEM_kyber_768_length_public_key; + break; + case KYBER_LEVEL5: + *len = OQS_KEM_kyber_1024_length_public_key; + break; + default: + /* No other values supported. */ + ret = BAD_FUNC_ARG; + break; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + (void)key; + if (ret == 0) { + *len = PQM4_PUBLIC_KEY_LENGTH; + } +#endif /* HAVE_PQM4 */ + + return ret; +} + +/** + * Get the size in bytes of cipher text for key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of cipher text in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_CipherTextSize(KyberKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + +#ifdef HAVE_LIBOQS + /* NOTE: SHAKE and AES variants have the same length ciphertext. */ + if (ret == 0) { + switch (key->type) { + case KYBER_LEVEL1: + *len = OQS_KEM_kyber_512_length_ciphertext; + break; + case KYBER_LEVEL3: + *len = OQS_KEM_kyber_768_length_ciphertext; + break; + case KYBER_LEVEL5: + *len = OQS_KEM_kyber_1024_length_ciphertext; + break; + default: + /* No other values supported. */ + ret = BAD_FUNC_ARG; + break; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + (void)key; + if (ret == 0) { + *len = PQM4_CIPHERTEXT_LENGTH; + } +#endif /* HAVE_PQM4 */ + + return ret; +} + +/** + * Size of a shared secret in bytes. Always KYBER_SS_SZ. + * + * @param [in] key Kyber key object. Not used. + * @param [out] Size of the shared secret created with a Kyber key. + * @return 0 on success. + * @return 0 to indicate success. + */ +int wc_KyberKey_SharedSecretSize(KyberKey* key, word32* len) +{ + (void)key; + /* Validate parameters. */ + if (len == NULL) { + return BAD_FUNC_ARG; + } + + *len = KYBER_SS_SZ; + + return 0; +} + +/******************************************************************************/ +/* Cryptographic operations. */ + +/** + * Make a Kyber key object using a random number generator. + * + * NOTE: rng is ignored. OQS and PQM4 don't use our RNG. + * + * @param [in, out] key Kyber key ovject. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or rng is NULL. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_KyberKey_MakeKey(KyberKey* key, WC_RNG* rng) +{ + int ret = 0; + const char* algName = NULL; + OQS_KEM *kem = NULL; + (void)rng; + + /* Validate parameter. */ + if (key == NULL) { + return BAD_FUNC_ARG; + } + +#ifdef HAVE_LIBOQS + if (ret == 0) { + algName = OQS_ID2name(key->type); + if (algName == NULL) { + ret = BAD_FUNC_ARG; + } + } + + if (ret == 0) { + algName = OQS_ID2name(key->type); + if (algName == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + kem = OQS_KEM_new(algName); + if (kem == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + if (OQS_KEM_keypair(kem, key->pub, key->priv) != + OQS_SUCCESS) { + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + if (ret == 0) { + if (crypto_kem_keypair(key->pub, key->priv) != 0) { + WOLFSSL_MSG("PQM4 keygen failure"); + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_PQM4 */ + + if (ret != 0) { + ForceZero(key, sizeof(*key)); + } + + return ret; +} + +/** + * Make a Kyber key object using random data. + * + * @param [in, out] key Kyber key ovject. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or rand is NULL. + * @return BUFFER_E when length is not KYBER_MAKEKEY_RAND_SZ. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, + int len) +{ + (void)rand; + (void)len; + /* OQS and PQM4 don't support external randomness. */ + return wc_KyberKey_MakeKey(key, NULL); +} + +/** + * Encapsulate with random number generator and derive secret. + * + * @param [in] key Kyber key object. + * @param [out] ct Cipher text. + * @param [out] ss Shared secret generated. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, ct, ss or RNG is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_KyberKey_Encapsulate(KyberKey* key, unsigned char* ct, unsigned char* ss, + WC_RNG* rng) +{ + int ret = 0; + const char * algName = NULL; + OQS_KEM *kem = NULL; + (void)rng; + + /* Validate parameters. */ + if ((key == NULL) || (ct == NULL) || (ss == NULL)) { + ret = BAD_FUNC_ARG; + } + +#ifdef HAVE_LIBOQS + if (ret == 0) { + algName = OQS_ID2name(key->type); + if (algName == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + kem = OQS_KEM_new(algName); + if (kem == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + if (OQS_KEM_encaps(kem, ct, ss, key->pub) != OQS_SUCCESS) { + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + if (ret == 0) { + if (crypto_kem_enc(ct, ss, key->pub) != 0) { + WOLFSSL_MSG("PQM4 Encapsulation failure."); + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_PQM4 */ + + return ret; +} + +/** + * Encapsulate with random data and derive secret. + * + * @param [out] ct Cipher text. + * @param [out] ss Shared secret generated. + * @param [in] rand Random data. + * @param [in] len Random data. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, ct, ss or RNG is NULL. + * @return BUFFER_E when len is not KYBER_ENC_RAND_SZ. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_KyberKey_EncapsulateWithRandom(KyberKey* key, unsigned char* ct, + unsigned char* ss, const unsigned char* rand, int len) +{ + (void)rand; + (void)len; + /* OQS and PQM4 don't support external randomness. */ + return wc_KyberKey_Encapsulate(key, ct, ss, NULL); +} + +/** + * Decapsulate the cipher text to calculate the shared secret. + * + * Validates the cipher text by encapsulating and comparing with data passed in. + * + * @param [in] key Kyber key object. + * @param [out] ss Shared secret. + * @param [in] ct Cipher text. + * @param [in] len Length of cipher text. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, ss or cr are NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the length of cipher text for the key type. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_KyberKey_Decapsulate(KyberKey* key, unsigned char* ss, + const unsigned char* ct, word32 len) +{ + int ret = 0; + const char * algName = NULL; + word32 ctlen = 0; + OQS_KEM *kem = NULL; + + /* Validate parameters. */ + if ((key == NULL) || (ss == NULL) || (ct == NULL)) { + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize(key, &ctlen); + } + if ((ret == 0) && (len != ctlen)) { + ret = BUFFER_E; + } + +#ifdef HAVE_LIBOQS + if (ret == 0) { + algName = OQS_ID2name(key->type); + if (algName == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + kem = OQS_KEM_new(algName); + if (kem == NULL) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + if (OQS_KEM_decaps(kem, ss, ct, key->priv) != OQS_SUCCESS) { + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_LIBOQS */ +#ifdef HAVE_PQM4 + if (ret == 0) { + if (crypto_kem_enc(ss, ct, key->priv) != 0) { + WOLFSSL_MSG("PQM4 Decapsulation failure."); + ret = BAD_FUNC_ARG; + } + } +#endif /* HAVE_PQM4 */ + + return ret; + +} + +/******************************************************************************/ +/* Encoding and decoding functions. */ + +/** + * Decode the private key. + * + * We store the whole thing in the private key buffer. Note this means we cannot + * do the encapsulation operation with the private key. But generally speaking + * this is never done. + * + * @param [in, out] key Kyber key object. + * @param [in] in Buffer holding encoded key. + * @param [in] len Length of data in buffer. + * @return 0 on success. + * @return BAD_FUNC_ARG when key ot in is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the correct size. + */ +int wc_KyberKey_DecodePrivateKey(KyberKey* key, unsigned char* in, word32 len) +{ + int ret = 0; + word32 privLen = 0; + + /* Validate parameters. */ + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(key, &privLen); + } + + /* Ensure the data is the correct length for the key type. */ + if ((ret == 0) && (len != privLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + XMEMCPY(key->priv, in, privLen); + } + + return ret; +} + +/** + * Decode public key. + * + * We store the whole thing in the public key buffer. + * + * @param [in, out] key Kyber key object. + * @param [in] in Buffer holding encoded key. + * @param [in] len Length of data in buffer. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or in is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the correct size. + */ +int wc_KyberKey_DecodePublicKey(KyberKey* key, unsigned char* in, word32 len) +{ + int ret = 0; + word32 pubLen = 0; + + /* Validate parameters. */ + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(key, &pubLen); + } + + /* Ensure the data is the correct length for the key type. */ + if ((ret == 0) && (len != pubLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + XMEMCPY(key->pub, in, pubLen); + } + + return ret; +} + +/** + * Encode the private key. + * + * We stored it as a blob so we can just copy it over. + * + * @param [in] key Kyber key object. + * @param [out] out Buffer to hold data. + * @param [in] len Size of buffer in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or out is NULL or private/public key not + * available. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_EncodePrivateKey(KyberKey* key, unsigned char* out, word32 len) +{ + int ret = 0; + unsigned int privLen = 0; + + if ((key == NULL) || (out == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(key, &privLen); + } + + /* Check buffer is big enough for encoding. */ + if ((ret == 0) && (len != privLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + XMEMCPY(out, key->priv, privLen); + } + + return ret; +} + +/** + * Encode the public key. + * + * We stored it as a blob so we can just copy it over. + * + * @param [in] key Kyber key object. + * @param [out] out Buffer to hold data. + * @param [in] len Size of buffer in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or out is NULL or public key not available. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_KyberKey_EncodePublicKey(KyberKey* key, unsigned char* out, word32 len) +{ + int ret = 0; + unsigned int pubLen = 0; + + if ((key == NULL) || (out == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(key, &pubLen); + } + + /* Check buffer is big enough for encoding. */ + if ((ret == 0) && (len != pubLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + XMEMCPY(out, key->pub, pubLen); + } + + return ret; +} + diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 4412af38d..60be38ba1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -271,6 +271,9 @@ #ifdef WOLFSSL_WC_KYBER #include #endif +#if defined(HAVE_LIBOQS) || defined(HAVE_PQM4) + #include +#endif #endif #ifdef WOLFCRYPT_HAVE_ECCSI #include @@ -29855,6 +29858,7 @@ WOLFSSL_TEST_SUBROUTINE int ed448_test(void) #endif /* HAVE_ED448 */ #ifdef WOLFSSL_HAVE_KYBER +#ifdef WOLFSSL_WC_KYBER /* OQS and PQM4 do not support KATs */ #ifdef WOLFSSL_KYBER512 static int kyber512_kat(void) { @@ -33701,6 +33705,7 @@ static int kyber1024_kat(void) return 0; } #endif /* WOLFSSL_KYBER1024 */ +#endif /* WOLFSSL_WC_KYBER */ WOLFSSL_TEST_SUBROUTINE int kyber_test(void) { @@ -33801,6 +33806,7 @@ WOLFSSL_TEST_SUBROUTINE int kyber_test(void) wc_FreeRng(&rng); +#ifdef WOLFSSL_WC_KYBER #ifdef WOLFSSL_KYBER512 ret = kyber512_kat(); if (ret != 0) @@ -33816,6 +33822,7 @@ WOLFSSL_TEST_SUBROUTINE int kyber_test(void) if (ret != 0) return ret; #endif +#endif /* WOLFSSL_WC_KYBER */ return 0; } diff --git a/wolfssl/wolfcrypt/ext_kyber.h b/wolfssl/wolfcrypt/ext_kyber.h new file mode 100644 index 000000000..08897b545 --- /dev/null +++ b/wolfssl/wolfcrypt/ext_kyber.h @@ -0,0 +1,62 @@ +/* ext_kyber.c + * + * Copyright (C) 2006-2022 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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-1335, USA + */ + +#ifndef EXT_KYBER_H +#define EXT_KYBER_H + +#include + +#if !defined(HAVE_LIBOQS) && !defined(HAVE_PQM4) +#error "This code requires liboqs or pqm4" +#endif + +#if defined(WOLFSSL_WC_KYBER) +#error "This code is incompatible with wolfCrypt's implementation of Kyber." +#endif + +#if defined (HAVE_LIBOQS) + #include + #define EXT_KYBER_MAX_PRIV_SZ OQS_KEM_kyber_1024_length_secret_key + #define EXT_KYBER_MAX_PUB_SZ OQS_KEM_kyber_1024_length_public_key +#elif defined(HAVE_PQM4) + #include "api_kyber.h" + #define PQM4_PUBLIC_KEY_LENGTH CRYPTO_PUBLICKEYBYTES + #define PQM4_PRIVATE_KEY_LENGTH CRYPTO_SECRETKEYBYTES + #define PQM4_SHARED_SECRET_LENGTH CRYPTO_BYTES + #define PQM4_CIPHERTEXT_LENGTH CRYPTO_CIPHERTEXTBYTES + + #define EXT_KYBER_MAX_PRIV_SZ PQM4_PRIVATE_KEY_LENGTH + #define EXT_KYBER_MAX_PUB_SZ PQM4_PUBLIC_KEY_LENGTH +#endif + +struct KyberKey { + /* Type of key: KYBER_LEVEL1 + * KYBER_LEVEL3 + * KYBER_LEVEL5 + * + * Note we don't save the variant (SHAKE vs AES) as that is decided at + * configuration time. */ + int type; + byte priv[EXT_KYBER_MAX_PRIV_SZ]; + byte pub[EXT_KYBER_MAX_PUB_SZ]; +}; + +#endif /* EXT_KYBER_H */ diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index ddbb036c7..543670b8a 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -73,7 +73,8 @@ nobase_include_HEADERS+= \ wolfssl/wolfcrypt/cpuid.h \ wolfssl/wolfcrypt/cryptocb.h \ wolfssl/wolfcrypt/kyber.h \ - wolfssl/wolfcrypt/wc_kyber.h + wolfssl/wolfcrypt/wc_kyber.h \ + wolfssl/wolfcrypt/ext_kyber.h noinst_HEADERS+= \ wolfssl/wolfcrypt/port/pic32/pic32mz-crypt.h \