Merge pull request #6564 from philljj/add_lms_hooks
Add LMS/HSS wolfCrypt hooks.
This commit is contained in:
commit
a026d843cf
55
INSTALL
55
INSTALL
@ -254,3 +254,58 @@
|
||||
The wolfssl port in vcpkg is kept up to date by wolfSSL.
|
||||
|
||||
We also have vcpkg ports for wolftpm, wolfmqtt and curl.
|
||||
|
||||
17. Building with hash-sigs lib for LMS/HSS support [EXPERIMENTAL]
|
||||
|
||||
Using LMS/HSS requires that the hash-sigs lib has been built on
|
||||
your system. We support hash-sigs lib at this git commit:
|
||||
b0631b8891295bf2929e68761205337b7c031726
|
||||
At the time of writing this, this is the HEAD of the master
|
||||
branch of the hash-sigs project.
|
||||
|
||||
Currently the hash-sigs project only builds static libraries:
|
||||
- hss_lib.a: a single-threaded static lib.
|
||||
- hss_lib_thread.a: a multi-threaded static lib.
|
||||
|
||||
The multi-threaded version will mainly have speedups for key
|
||||
generation and signing.
|
||||
|
||||
Additionally, the hash-sigs project can be modified to build
|
||||
and install a shared library in /usr/local with either single
|
||||
or multi-threaded versions. If the shared version has been
|
||||
built, libhss.so is the assumed name.
|
||||
|
||||
wolfSSL supports either option, and by default will look for
|
||||
hss_lib.a first, and hss_lib_thread.a second, and libhss.so
|
||||
lastly, in a specified hash-sigs dir.
|
||||
|
||||
How to get and build the hash-sigs library:
|
||||
$ mkdir ~/hash_sigs
|
||||
$ cd ~/hash_sigs
|
||||
$ git clone https://github.com/cisco/hash-sigs.git src
|
||||
$ cd src
|
||||
$ git checkout b0631b8891295bf2929e68761205337b7c031726
|
||||
|
||||
In sha256.h, set USE_OPENSSL to 0:
|
||||
#define USE_OPENSSL 0
|
||||
|
||||
To build the single-threaded version:
|
||||
$ make hss_lib.a
|
||||
$ ls *.a
|
||||
hss_lib.a
|
||||
|
||||
To build multi-threaded:
|
||||
$ make hss_lib_thread.a
|
||||
$ ls *.a
|
||||
hss_lib_thread.a
|
||||
|
||||
Build wolfSSL with
|
||||
$ ./configure \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--enable-lms=yes \
|
||||
--with-liblms=<path to dir containing hss_lib_thread.a>
|
||||
$ make
|
||||
|
||||
Run the benchmark against LMS/HSS with:
|
||||
$ ./wolfcrypt/benchmark/benchmark -lms_hss
|
||||
|
108
configure.ac
108
configure.ac
@ -1144,6 +1144,109 @@ then
|
||||
fi
|
||||
|
||||
|
||||
# liblms
|
||||
# Get the path to the hash-sigs LMS HSS lib.
|
||||
ENABLED_LIBLMS="no"
|
||||
tryliblmsdir=""
|
||||
AC_ARG_WITH([liblms],
|
||||
[AS_HELP_STRING([--with-liblms=PATH],[PATH to hash-sigs LMS/HSS install (default /usr/local) EXPERIMENTAL!])],
|
||||
[
|
||||
AC_MSG_CHECKING([for liblms])
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <hss.h>]], [[ param_set_t lm_type; param_set_t lm_ots_type; hss_get_public_key_len(4, &lm_type, &lm_ots_type); ]])], [ liblms_linked=yes ],[ liblms_linked=no ])
|
||||
|
||||
if test "x$liblms_linked" = "xno" ; then
|
||||
if test "x$withval" != "xno" ; then
|
||||
tryliblmsdir=$withval
|
||||
fi
|
||||
if test "x$withval" = "xyes" ; then
|
||||
tryliblmsdir="/usr/local"
|
||||
fi
|
||||
|
||||
# 1. By default use the hash-sigs single-threaded static library.
|
||||
# 2. If 1 not found, then use the multi-threaded static lib.
|
||||
# 3. If 2 not found, then use the multi-threaded dynamic lib.
|
||||
if test -e $tryliblmsdir/hss_lib.a; then
|
||||
CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir"
|
||||
LIB_STATIC_ADD="$LIB_STATIC_ADD $tryliblmsdir/hss_lib.a"
|
||||
enable_shared=no
|
||||
enable_static=yes
|
||||
liblms_linked=yes
|
||||
elif test -e $tryliblmsdir/hss_lib_thread.a; then
|
||||
CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir"
|
||||
LIB_STATIC_ADD="$LIB_STATIC_ADD $tryliblmsdir/hss_lib_thread.a"
|
||||
enable_shared=no
|
||||
enable_static=yes
|
||||
liblms_linked=yes
|
||||
elif test -e $tryliblmsdir/lib/libhss.so; then
|
||||
LIBS="$LIBS -lhss"
|
||||
CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir/include/hss"
|
||||
LDFLAGS="$AM_LDFLAGS $LDFLAGS -L$tryliblmsdir/lib"
|
||||
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <hss.h>]], [[ param_set_t lm_type; param_set_t lm_ots_type; hss_get_public_key_len(4, &lm_type, &lm_ots_type); ]])], [ liblms_linked=yes ],[ liblms_linked=no ])
|
||||
else
|
||||
AC_MSG_ERROR([liblms isn't found.
|
||||
If it's already installed, specify its path using --with-liblms=/dir/])
|
||||
fi
|
||||
|
||||
if test "x$liblms_linked" = "xno" ; then
|
||||
AC_MSG_ERROR([liblms isn't found.
|
||||
If it's already installed, specify its path using --with-liblms=/dir/])
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([yes])
|
||||
AM_CPPFLAGS="$CPPFLAGS"
|
||||
AM_LDFLAGS="$LDFLAGS"
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
|
||||
AM_CFLAGS="$AM_CFLAGS -DHAVE_LIBLMS"
|
||||
ENABLED_LIBLMS="yes"
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# LMS
|
||||
AC_ARG_ENABLE([lms],
|
||||
[AS_HELP_STRING([--enable-lms],[Enable stateful LMS/HSS signatures (default: disabled)])],
|
||||
[ ENABLED_LMS=$enableval ],
|
||||
[ ENABLED_LMS=no ]
|
||||
)
|
||||
|
||||
ENABLED_WC_LMS=no
|
||||
for v in `echo $ENABLED_LMS | tr "," " "`
|
||||
do
|
||||
case $v in
|
||||
yes)
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
wolfssl)
|
||||
ENABLED_WC_LMS=yes
|
||||
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([Invalid choice for LMS []: $ENABLED_LMS.])
|
||||
break;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test "$ENABLED_LMS" != "no"
|
||||
then
|
||||
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_LMS"
|
||||
|
||||
if test "$ENABLED_WC_LMS" = "no";
|
||||
then
|
||||
# Default is to use hash-sigs LMS lib. Make sure it's enabled.
|
||||
if test "$ENABLED_LIBLMS" = "no"; then
|
||||
AC_MSG_ERROR([The default implementation for LMS is the hash-sigs LMS/HSS lib.
|
||||
Please use --with-liblms.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# SINGLE THREADED
|
||||
AC_ARG_ENABLE([singlethreaded],
|
||||
[AS_HELP_STRING([--enable-singlethreaded],[Enable wolfSSL single threaded (default: disabled)])],
|
||||
@ -8753,6 +8856,7 @@ AM_CONDITIONAL([BUILD_FE448], [test "x$ENABLED_FE448" = "xyes" || test "x$ENABLE
|
||||
AM_CONDITIONAL([BUILD_GE448], [test "x$ENABLED_GE448" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_CURVE448],[test "x$ENABLED_CURVE448" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_CURVE448_SMALL],[test "x$ENABLED_CURVE448_SMALL" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_WC_LMS],[test "x$ENABLED_WC_LMS" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_WC_KYBER],[test "x$ENABLED_WC_KYBER" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_ECCSI],[test "x$ENABLED_ECCSI" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_SAKKE],[test "x$ENABLED_SAKKE" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
@ -8792,6 +8896,7 @@ AM_CONDITIONAL([BUILD_CRL],[test "x$ENABLED_CRL" != "xno" || test "x$ENABLED_USE
|
||||
AM_CONDITIONAL([BUILD_CRL_MONITOR],[test "x$ENABLED_CRL_MONITOR" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_USER_RSA],[test "x$ENABLED_USER_RSA" = "xyes"] )
|
||||
AM_CONDITIONAL([BUILD_USER_CRYPTO],[test "x$ENABLED_USER_CRYPTO" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_LIBLMS],[test "x$ENABLED_LIBLMS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_LIBOQS],[test "x$ENABLED_LIBOQS" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_WNR],[test "x$ENABLED_WNR" = "xyes"])
|
||||
AM_CONDITIONAL([BUILD_SRP],[test "x$ENABLED_SRP" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
|
||||
@ -9242,6 +9347,8 @@ echo " * ED25519 streaming: $ENABLED_ED25519_STREAM"
|
||||
echo " * CURVE448: $ENABLED_CURVE448"
|
||||
echo " * ED448: $ENABLED_ED448"
|
||||
echo " * ED448 streaming: $ENABLED_ED448_STREAM"
|
||||
echo " * LMS: $ENABLED_LMS"
|
||||
echo " * LMS wolfSSL impl: $ENABLED_WC_LMS"
|
||||
echo " * KYBER: $ENABLED_KYBER"
|
||||
echo " * KYBER wolfSSL impl: $ENABLED_WC_KYBER"
|
||||
echo " * ECCSI $ENABLED_ECCSI"
|
||||
@ -9297,6 +9404,7 @@ echo " * Persistent session cache: $ENABLED_SAVESESSION"
|
||||
echo " * Persistent cert cache: $ENABLED_SAVECERT"
|
||||
echo " * Atomic User Record Layer: $ENABLED_ATOMICUSER"
|
||||
echo " * Public Key Callbacks: $ENABLED_PKCALLBACKS"
|
||||
echo " * liblms: $ENABLED_LIBLMS"
|
||||
echo " * liboqs: $ENABLED_LIBOQS"
|
||||
echo " * Whitewood netRandom: $ENABLED_WNR"
|
||||
echo " * Server Name Indication: $ENABLED_SNI"
|
||||
|
@ -655,6 +655,10 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_WC_LMS
|
||||
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wc_lms.c
|
||||
endif
|
||||
|
||||
if BUILD_CURVE25519
|
||||
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve25519.c
|
||||
endif
|
||||
@ -734,6 +738,10 @@ src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/sphincs.c
|
||||
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_kyber.c
|
||||
endif
|
||||
|
||||
if BUILD_LIBLMS
|
||||
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_lms.c
|
||||
endif
|
||||
|
||||
if BUILD_LIBZ
|
||||
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/compress.c
|
||||
endif
|
||||
|
@ -154,6 +154,12 @@
|
||||
#include <wolfssl/wolfcrypt/ext_kyber.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#include <wolfssl/wolfcrypt/lms.h>
|
||||
#ifdef HAVE_LIBLMS
|
||||
#include <wolfssl/wolfcrypt/ext_lms.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_HAVE_ECCSI
|
||||
#include <wolfssl/wolfcrypt/eccsi.h>
|
||||
#endif
|
||||
@ -565,6 +571,9 @@
|
||||
#define BENCH_SPHINCS_SMALL_LEVEL3_SIGN 0x00000010
|
||||
#define BENCH_SPHINCS_SMALL_LEVEL5_SIGN 0x00000020
|
||||
|
||||
/* Post-Quantum Stateful Hash-Based sig algorithms. */
|
||||
#define BENCH_LMS_HSS 0x00000001
|
||||
|
||||
/* Other */
|
||||
#define BENCH_RNG 0x00000001
|
||||
#define BENCH_SCRYPT 0x00000002
|
||||
@ -603,6 +612,8 @@ static word32 bench_pq_asym_algs = 0;
|
||||
static word32 bench_pq_asym_algs2 = 0;
|
||||
/* Other cryptographic algorithms to benchmark. */
|
||||
static word32 bench_other_algs = 0;
|
||||
/* Post-Quantum Stateful Hash-Based sig algorithms to benchmark. */
|
||||
static word32 bench_pq_hash_sig_algs = 0;
|
||||
|
||||
#if !defined(WOLFSSL_BENCHMARK_ALL) && !defined(NO_MAIN_DRIVER)
|
||||
|
||||
@ -856,6 +867,21 @@ static const bench_alg bench_other_opt[] = {
|
||||
|
||||
#endif /* !WOLFSSL_BENCHMARK_ALL && !NO_MAIN_DRIVER */
|
||||
|
||||
#if defined(WOLFSSL_HAVE_LMS)
|
||||
typedef struct bench_pq_hash_sig_alg {
|
||||
/* Command line option string. */
|
||||
const char* str;
|
||||
/* Bit values to set. */
|
||||
word32 val;
|
||||
} bench_pq_hash_sig_alg;
|
||||
|
||||
static const bench_pq_hash_sig_alg bench_pq_hash_sig_opt[] = {
|
||||
{ "-pq_hash_sig", 0xffffffff},
|
||||
{ "-lms_hss", BENCH_LMS_HSS},
|
||||
{ NULL, 0}
|
||||
};
|
||||
#endif /* if defined(WOLFSSL_HAVE_LMS) */
|
||||
|
||||
#if defined(HAVE_PQC) && defined(HAVE_LIBOQS)
|
||||
/* The post-quantum-specific mapping of command line option to bit values and
|
||||
* OQS name. */
|
||||
@ -1592,6 +1618,7 @@ static void benchmark_static_init(int force)
|
||||
bench_asym_algs = 0;
|
||||
bench_pq_asym_algs = 0;
|
||||
bench_other_algs = 0;
|
||||
bench_pq_hash_sig_algs = 0;
|
||||
csv_format = 0;
|
||||
}
|
||||
}
|
||||
@ -2798,6 +2825,12 @@ static void* benchmarks_do(void* args)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
if (bench_all || (bench_pq_hash_sig_algs & BENCH_LMS_HSS)) {
|
||||
bench_lms();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
if (bench_all || (bench_asym_algs & BENCH_ECC_MAKEKEY) ||
|
||||
(bench_asym_algs & BENCH_ECC) ||
|
||||
@ -7624,6 +7657,375 @@ void bench_kyber(int type)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
/* WC_LMS_PARM_L2_H10_W2
|
||||
* signature length: 9300 */
|
||||
static const byte lms_priv_L2_H10_W2[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x62,0x62,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xC7,0x74,0x25,0x5B,0x2C,0xE8,0xDA,0x53,
|
||||
0xF0,0x7C,0x04,0x3F,0x64,0x2D,0x26,0x2C,
|
||||
0x46,0x1D,0xC8,0x90,0x77,0x59,0xD6,0xC0,
|
||||
0x56,0x46,0x7D,0x97,0x64,0xF2,0xA3,0xA1,
|
||||
0xF8,0xD0,0x3B,0x5F,0xAC,0x40,0xB9,0x9E,
|
||||
0x83,0x67,0xBF,0x92,0x8D,0xFE,0x45,0x79
|
||||
};
|
||||
|
||||
static const byte lms_pub_L2_H10_W2[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x06,
|
||||
0x00,0x00,0x00,0x02,0xF8,0xD0,0x3B,0x5F,
|
||||
0xAC,0x40,0xB9,0x9E,0x83,0x67,0xBF,0x92,
|
||||
0x8D,0xFE,0x45,0x79,0x41,0xBC,0x2A,0x3B,
|
||||
0x9F,0xC0,0x11,0x12,0x93,0xF0,0x5A,0xA5,
|
||||
0xC1,0x88,0x29,0x79,0x6C,0x3E,0x0A,0x0F,
|
||||
0xEC,0x3B,0x3E,0xE4,0x38,0xD3,0xD2,0x34,
|
||||
0x7F,0xC8,0x91,0xB0
|
||||
};
|
||||
|
||||
/* WC_LMS_PARM_L2_H10_W4
|
||||
* signature length: 5076 */
|
||||
static const byte lms_priv_L2_H10_W4[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x63,0x63,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xAE,0x28,0x87,0x19,0x4F,0x4B,0x68,0x61,
|
||||
0x93,0x9A,0xC7,0x0E,0x33,0xB8,0xCE,0x96,
|
||||
0x66,0x0D,0xC7,0xB1,0xFA,0x94,0x80,0xA2,
|
||||
0x28,0x9B,0xCF,0xE2,0x08,0xB5,0x25,0xAC,
|
||||
0xFB,0xB8,0x65,0x5E,0xD1,0xCC,0x31,0xDA,
|
||||
0x2E,0x49,0x3A,0xEE,0xAF,0x63,0x70,0x5E
|
||||
};
|
||||
|
||||
static const byte lms_pub_L2_H10_W4[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x06,
|
||||
0x00,0x00,0x00,0x03,0xFB,0xB8,0x65,0x5E,
|
||||
0xD1,0xCC,0x31,0xDA,0x2E,0x49,0x3A,0xEE,
|
||||
0xAF,0x63,0x70,0x5E,0xA2,0xD5,0xB6,0x15,
|
||||
0x33,0x8C,0x9B,0xE9,0xE1,0x91,0x40,0x1A,
|
||||
0x12,0xE0,0xD7,0xBD,0xE4,0xE0,0x76,0xF5,
|
||||
0x04,0x90,0x76,0xA5,0x9A,0xA7,0x4E,0xFE,
|
||||
0x6B,0x9A,0xD3,0x14
|
||||
};
|
||||
|
||||
/* WC_LMS_PARM_L3_H5_W4
|
||||
* signature length: 7160 */
|
||||
static const byte lms_priv_L3_H5_W4[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x53,0x53,0x53,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0x38,0xD1,0xBE,0x68,0xD1,0x93,0xE1,0x14,
|
||||
0x6C,0x8B,0xED,0xE2,0x25,0x88,0xED,0xAC,
|
||||
0x57,0xBD,0x87,0x9F,0x54,0xF3,0x58,0xD9,
|
||||
0x4D,0xF5,0x6A,0xBD,0x71,0x99,0x6A,0x28,
|
||||
0x2F,0xE1,0xFC,0xD1,0xD1,0x0C,0x7C,0xF8,
|
||||
0xB4,0xDC,0xDF,0x7F,0x14,0x1A,0x7B,0x50
|
||||
};
|
||||
|
||||
static const byte lms_pub_L3_H5_W4[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,
|
||||
0x00,0x00,0x00,0x03,0x2F,0xE1,0xFC,0xD1,
|
||||
0xD1,0x0C,0x7C,0xF8,0xB4,0xDC,0xDF,0x7F,
|
||||
0x14,0x1A,0x7B,0x50,0x8E,0x3A,0xD4,0x05,
|
||||
0x0C,0x95,0x59,0xA0,0xCA,0x7A,0xD8,0xD6,
|
||||
0x5D,0xBD,0x42,0xBB,0xD5,0x82,0xB8,0x9C,
|
||||
0x52,0x37,0xB7,0x45,0x03,0xC2,0x06,0xCE,
|
||||
0xAB,0x4B,0x51,0x39
|
||||
};
|
||||
|
||||
/* WC_LMS_PARM_L3_H5_W8
|
||||
* signature length: 3992 */
|
||||
static const byte lms_priv_L3_H5_W8[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x54,0x54,0x54,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xA5,0x46,0x97,0x0C,0xA1,0x3C,0xEA,0x17,
|
||||
0x5C,0x9D,0x59,0xF4,0x0E,0x27,0x37,0xF3,
|
||||
0x6A,0x1C,0xF7,0x29,0x4A,0xCC,0xCD,0x7B,
|
||||
0x4F,0xE7,0x37,0x6E,0xEF,0xC1,0xBD,0xBD,
|
||||
0x04,0x5D,0x8E,0xDD,0xAA,0x47,0xCC,0xE6,
|
||||
0xCE,0x78,0x46,0x20,0x41,0x87,0xE0,0x85
|
||||
};
|
||||
|
||||
static const byte lms_pub_L3_H5_W8[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,
|
||||
0x00,0x00,0x00,0x04,0x04,0x5D,0x8E,0xDD,
|
||||
0xAA,0x47,0xCC,0xE6,0xCE,0x78,0x46,0x20,
|
||||
0x41,0x87,0xE0,0x85,0x0D,0x2C,0x46,0xB9,
|
||||
0x39,0x8C,0xA3,0x92,0x4F,0xCE,0x50,0x96,
|
||||
0x90,0x9C,0xF3,0x36,0x2E,0x09,0x15,0x3B,
|
||||
0x4B,0x34,0x17,0xE7,0xE2,0x55,0xFC,0x5B,
|
||||
0x83,0xAB,0x43,0xAF
|
||||
};
|
||||
|
||||
/* WC_LMS_PARM_L3_H10_W4
|
||||
* signature length: 7640 */
|
||||
static const byte lms_priv_L3_H10_W4[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x63,0x63,0x63,0xFF,0xFF,0xFF,0xFF,0xFF,
|
||||
0xDF,0x98,0xAB,0xEC,0xFE,0x13,0x9F,0xF8,
|
||||
0xD7,0x2B,0x4F,0x4C,0x79,0x34,0xB8,0x89,
|
||||
0x24,0x6B,0x26,0x7D,0x7A,0x2E,0xA2,0xCB,
|
||||
0x82,0x75,0x4E,0x96,0x54,0x49,0xED,0xA0,
|
||||
0xAF,0xC7,0xA5,0xEE,0x8A,0xA2,0x83,0x99,
|
||||
0x4B,0x18,0x59,0x2B,0x66,0xC0,0x32,0xDB
|
||||
};
|
||||
|
||||
static const byte lms_pub_L3_H10_W4[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x06,
|
||||
0x00,0x00,0x00,0x03,0xAF,0xC7,0xA5,0xEE,
|
||||
0x8A,0xA2,0x83,0x99,0x4B,0x18,0x59,0x2B,
|
||||
0x66,0xC0,0x32,0xDB,0xC4,0x18,0xEB,0x11,
|
||||
0x17,0x7D,0xAA,0x93,0xFD,0xA0,0x70,0x4D,
|
||||
0x68,0x4B,0x63,0x8F,0xC2,0xE7,0xCA,0x34,
|
||||
0x14,0x31,0x0D,0xAA,0x18,0xBF,0x9B,0x32,
|
||||
0x8D,0x78,0xD5,0xA8
|
||||
};
|
||||
|
||||
/* WC_LMS_PARM_L4_H5_W8
|
||||
* signature length: 5340 */
|
||||
static const byte lms_priv_L4_H5_W8[64] =
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x54,0x54,0x54,0x54,0xFF,0xFF,0xFF,0xFF,
|
||||
0x46,0x8F,0x2A,0x4A,0x14,0x26,0xF0,0x89,
|
||||
0xFE,0xED,0x66,0x0F,0x73,0x69,0xB1,0x4C,
|
||||
0x47,0xA1,0x35,0x9F,0x7B,0xBA,0x08,0x03,
|
||||
0xEE,0xA2,0xEB,0xAD,0xB4,0x82,0x52,0x1F,
|
||||
0xFD,0x9B,0x22,0x82,0x42,0x1A,0x96,0x1E,
|
||||
0xE4,0xA1,0x9C,0x33,0xED,0xE6,0x9F,0xAB
|
||||
};
|
||||
|
||||
static const byte lms_pub_L4_H5_W8[60] =
|
||||
{
|
||||
0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x05,
|
||||
0x00,0x00,0x00,0x04,0xFD,0x9B,0x22,0x82,
|
||||
0x42,0x1A,0x96,0x1E,0xE4,0xA1,0x9C,0x33,
|
||||
0xED,0xE6,0x9F,0xAB,0x6B,0x47,0x05,0x5B,
|
||||
0xA7,0xAD,0xF6,0x88,0xA5,0x4F,0xCD,0xF1,
|
||||
0xDA,0x29,0x67,0xC3,0x7F,0x2C,0x11,0xFE,
|
||||
0x85,0x1A,0x7A,0xD8,0xD5,0x46,0x74,0x3B,
|
||||
0x74,0x24,0x12,0xC8
|
||||
};
|
||||
|
||||
static int lms_write_key_mem(const byte * priv, word32 privSz, void *context)
|
||||
{
|
||||
/* WARNING: THIS IS AN INSECURE WRITE CALLBACK THAT SHOULD ONLY
|
||||
* BE USED FOR TESTING PURPOSES! Production applications should
|
||||
* write only to non-volatile storage. */
|
||||
XMEMCPY(context, priv, privSz);
|
||||
return WC_LMS_RC_SAVED_TO_NV_MEMORY;
|
||||
}
|
||||
|
||||
static int lms_read_key_mem(byte * priv, word32 privSz, void *context)
|
||||
{
|
||||
/* WARNING: THIS IS AN INSECURE READ CALLBACK THAT SHOULD ONLY
|
||||
* BE USED FOR TESTING PURPOSES! */
|
||||
XMEMCPY(priv, context, privSz);
|
||||
return WC_LMS_RC_READ_TO_MEMORY;
|
||||
}
|
||||
|
||||
static void bench_lms_sign_verify(enum wc_LmsParm parm)
|
||||
{
|
||||
LmsKey key;
|
||||
int ret = 0;
|
||||
const char * msg = TEST_STRING;
|
||||
word32 msgSz = TEST_STRING_SZ;
|
||||
byte * sig = NULL;
|
||||
word32 sigSz = 0;
|
||||
word32 privLen = 0;
|
||||
int loaded = 0;
|
||||
int times = 0;
|
||||
int count = 0;
|
||||
double start = 0.0F;
|
||||
byte priv[HSS_MAX_PRIVATE_KEY_LEN];
|
||||
const char * str = wc_LmsKey_ParmToStr(parm);
|
||||
|
||||
ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_Init failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
ret = wc_LmsKey_SetLmsParm(&key, parm);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_SetLmsParm failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
switch (parm) {
|
||||
case WC_LMS_PARM_L2_H10_W2:
|
||||
XMEMCPY(priv, lms_priv_L2_H10_W2, sizeof(lms_priv_L2_H10_W2));
|
||||
XMEMCPY(key.pub, lms_pub_L2_H10_W2, sizeof(lms_pub_L2_H10_W2));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W4:
|
||||
XMEMCPY(priv, lms_priv_L2_H10_W4, sizeof(lms_priv_L2_H10_W4));
|
||||
XMEMCPY(key.pub, lms_pub_L2_H10_W4, sizeof(lms_pub_L2_H10_W4));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W4:
|
||||
XMEMCPY(priv, lms_priv_L3_H5_W4, sizeof(lms_priv_L3_H5_W4));
|
||||
XMEMCPY(key.pub, lms_pub_L3_H5_W4, sizeof(lms_pub_L3_H5_W4));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W8:
|
||||
XMEMCPY(priv, lms_priv_L3_H5_W8, sizeof(lms_priv_L3_H5_W8));
|
||||
XMEMCPY(key.pub, lms_pub_L3_H5_W8, sizeof(lms_pub_L3_H5_W8));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_L3_H10_W4:
|
||||
XMEMCPY(priv, lms_priv_L3_H10_W4, sizeof(lms_priv_L3_H10_W4));
|
||||
XMEMCPY(key.pub, lms_pub_L3_H10_W4, sizeof(lms_pub_L3_H10_W4));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_L4_H5_W8:
|
||||
XMEMCPY(priv, lms_priv_L4_H5_W8, sizeof(lms_priv_L4_H5_W8));
|
||||
XMEMCPY(key.pub, lms_pub_L4_H5_W8, sizeof(lms_pub_L4_H5_W8));
|
||||
break;
|
||||
|
||||
case WC_LMS_PARM_NONE:
|
||||
case WC_LMS_PARM_L1_H15_W2:
|
||||
case WC_LMS_PARM_L1_H15_W4:
|
||||
case WC_LMS_PARM_L2_H10_W8:
|
||||
case WC_LMS_PARM_L3_H5_W2:
|
||||
printf("bench_lms_sign_verify: unsupported benchmark option: %d\n",
|
||||
parm);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
ret = wc_LmsKey_SetWriteCb(&key, lms_write_key_mem);
|
||||
if (ret) {
|
||||
fprintf(stderr, "error: wc_LmsKey_SetWriteCb failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
ret = wc_LmsKey_SetReadCb(&key, lms_read_key_mem);
|
||||
if (ret) {
|
||||
fprintf(stderr, "error: wc_LmsKey_SetReadCb failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
ret = wc_LmsKey_SetContext(&key, (void *) priv);
|
||||
if (ret) {
|
||||
fprintf(stderr, "error: wc_LmsKey_SetContext failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
/* Even with saved priv/pub keys, we must still reload the private
|
||||
* key before using it. Reloading the private key is the bottleneck
|
||||
* for larger heights. Only print load time in debug builds. */
|
||||
#if defined(DEBUG_WOLFSSL)
|
||||
bench_stats_start(&count, &start);
|
||||
#endif /* if defined DEBUG_WOLFSSL*/
|
||||
|
||||
ret = wc_LmsKey_Reload(&key);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_Reload failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
count +=1;
|
||||
|
||||
ret = wc_LmsKey_GetSigLen(&key, &sigSz);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_GetSigLen failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
ret = wc_LmsKey_GetPrivLen(&key, &privLen);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_GetPrivLen failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
#if defined(DEBUG_WOLFSSL)
|
||||
bench_stats_check(start);
|
||||
bench_stats_asym_finish(str, (int)privLen, "load", 0,
|
||||
count, start, ret);
|
||||
#endif /* if defined DEBUG_WOLFSSL*/
|
||||
|
||||
loaded = 1;
|
||||
|
||||
sig = XMALLOC(sigSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (sig == NULL) {
|
||||
printf("bench_lms_sign_verify malloc failed\n");
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
bench_stats_start(&count, &start);
|
||||
|
||||
do {
|
||||
/* LMS is stateful. Async queuing not practical. */
|
||||
for (times = 0; times < ntimes; ++times) {
|
||||
|
||||
ret = wc_LmsKey_Sign(&key, sig, &sigSz, (byte *) msg, msgSz);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_Sign failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
}
|
||||
|
||||
count += times;
|
||||
} while (bench_stats_check(start));
|
||||
|
||||
bench_stats_asym_finish(str, (int)sigSz, "sign", 0,
|
||||
count, start, ret);
|
||||
|
||||
count = 0;
|
||||
bench_stats_start(&count, &start);
|
||||
|
||||
do {
|
||||
/* LMS is stateful. Async queuing not practical. */
|
||||
for (times = 0; times < ntimes; ++times) {
|
||||
ret = wc_LmsKey_Verify(&key, sig, sigSz, (byte *) msg, msgSz);
|
||||
if (ret) {
|
||||
printf("wc_LmsKey_Verify failed: %d\n", ret);
|
||||
goto exit_lms_sign_verify;
|
||||
}
|
||||
}
|
||||
|
||||
count += times;
|
||||
} while (bench_stats_check(start));
|
||||
|
||||
exit_lms_sign_verify:
|
||||
bench_stats_asym_finish(str, (int)sigSz, "verify", 0,
|
||||
count, start, ret);
|
||||
|
||||
|
||||
if (loaded) {
|
||||
wc_LmsKey_Free(&key);
|
||||
loaded = 0;
|
||||
}
|
||||
|
||||
if (sig != NULL) {
|
||||
XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
sig = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void bench_lms(void)
|
||||
{
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L2_H10_W2);
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L2_H10_W4);
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L3_H5_W4);
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L3_H5_W8);
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L3_H10_W4);
|
||||
bench_lms_sign_verify(WC_LMS_PARM_L4_H5_W8);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* ifdef WOLFSSL_HAVE_LMS */
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
|
||||
/* Maximum ECC name plus null terminator:
|
||||
@ -9952,6 +10354,10 @@ static void Usage(void)
|
||||
print_alg(bench_pq_asym_opt2[i].str, &line);
|
||||
#endif /* HAVE_LIBOQS */
|
||||
#endif /* HAVE_PQC */
|
||||
#if defined(WOLFSSL_HAVE_LMS)
|
||||
for (i=0; bench_pq_hash_sig_opt[i].str != NULL; i++)
|
||||
print_alg(bench_pq_hash_sig_opt[i].str, &line);
|
||||
#endif /* if defined(WOLFSSL_HAVE_LMS) */
|
||||
printf("\n");
|
||||
#endif /* !WOLFSSL_BENCHMARK_ALL */
|
||||
e++;
|
||||
@ -10211,6 +10617,17 @@ int wolfcrypt_benchmark_main(int argc, char** argv)
|
||||
optMatched = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_HAVE_LMS)
|
||||
/* post-quantum stateful hash-based signatures */
|
||||
for (i=0; !optMatched && bench_pq_hash_sig_opt[i].str != NULL; i++) {
|
||||
if (string_matches(argv[1], bench_pq_hash_sig_opt[i].str)) {
|
||||
bench_pq_hash_sig_algs |= bench_pq_hash_sig_opt[i].val;
|
||||
bench_all = 0;
|
||||
optMatched = 1;
|
||||
}
|
||||
}
|
||||
#endif /* if defined(WOLFSSL_HAVE_LMS) */
|
||||
#endif
|
||||
if (!optMatched) {
|
||||
printf("Option not recognized: %s\n", argv[1]);
|
||||
|
@ -101,6 +101,7 @@ void bench_rsa(int useDeviceID);
|
||||
void bench_rsa_key(int useDeviceID, word32 keySz);
|
||||
void bench_dh(int useDeviceID);
|
||||
void bench_kyber(int type);
|
||||
void bench_lms(void);
|
||||
void bench_ecc_curve(int curveId);
|
||||
void bench_eccMakeKey(int useDeviceID, int curveId);
|
||||
void bench_ecc(int useDeviceID, int curveId);
|
||||
|
818
wolfcrypt/src/ext_lms.c
Normal file
818
wolfcrypt/src/ext_lms.c
Normal file
@ -0,0 +1,818 @@
|
||||
/* ext_lms.c
|
||||
*
|
||||
* Copyright (C) 2006-2023 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
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/wolfcrypt/error-crypt.h>
|
||||
#include <wolfssl/wolfcrypt/logging.h>
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#include <wolfssl/wolfcrypt/ext_lms.h>
|
||||
|
||||
#ifdef NO_INLINE
|
||||
#include <wolfssl/wolfcrypt/misc.h>
|
||||
#else
|
||||
#define WOLFSSL_MISC_INCLUDED
|
||||
#include <wolfcrypt/src/misc.c>
|
||||
#endif
|
||||
|
||||
/* If built against hss_lib_thread.a, the hash-sigs lib will spawn
|
||||
* worker threads to parallelize cpu intensive tasks. This will mainly
|
||||
* speedup key generation and signing, and to a lesser extent
|
||||
* verifying for larger levels values.
|
||||
*
|
||||
* Their default max is 16 worker threads, but can be capped with
|
||||
* hss_extra_info_set_threads(). To be safe we are capping at 4 here.
|
||||
* */
|
||||
#define EXT_LMS_MAX_THREADS (4)
|
||||
|
||||
/* The hash-sigs hss_generate_private_key API requires a generate_random
|
||||
* callback that only has output and length args. The RNG struct must be global
|
||||
* to the function. Maybe there should be a wc_LmsKey_SetRngCb. */
|
||||
static THREAD_LS_T WC_RNG * LmsRng = NULL;
|
||||
|
||||
static bool LmsGenerateRand(void * output, size_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (output == NULL || LmsRng == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ret = wc_RNG_GenerateBlock(LmsRng, output, (word32) length);
|
||||
|
||||
if (ret) {
|
||||
WOLFSSL_MSG("error: LmsGenerateRand failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Write callback passed into hash-sigs hss lib.
|
||||
*
|
||||
* Returns true on success. */
|
||||
static bool LmsWritePrivKey(unsigned char *private_key,
|
||||
size_t len_private_key, void *lmsKey)
|
||||
{
|
||||
LmsKey * key = (LmsKey *) lmsKey;
|
||||
enum wc_LmsRc ret = WC_LMS_RC_NONE;
|
||||
|
||||
if (private_key == NULL || key == NULL || len_private_key <= 0) {
|
||||
WOLFSSL_MSG("error: LmsWritePrivKey: invalid args");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key->state != WC_LMS_STATE_PARMSET && key->state != WC_LMS_STATE_OK) {
|
||||
/* The LmsKey is not ready for writing. */
|
||||
WOLFSSL_MSG("error: LmsWritePrivKey: LMS key not in writeable state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key->write_private_key == NULL) {
|
||||
WOLFSSL_MSG("error: LmsWritePrivKey: LMS key write callback not set");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Use write callback that saves private key to non-volatile storage. */
|
||||
ret = key->write_private_key(private_key, len_private_key, key->context);
|
||||
|
||||
if (ret != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
|
||||
WOLFSSL_MSG("error: LmsKey write_private_key failed");
|
||||
WOLFSSL_MSG(wc_LmsKey_RcToStr(ret));
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Read callback passed into hash-sigs hss lib.
|
||||
*
|
||||
* Returns true on success. */
|
||||
static bool LmsReadPrivKey(unsigned char *private_key,
|
||||
size_t len_private_key, void *lmsKey)
|
||||
{
|
||||
LmsKey * key = (LmsKey *) lmsKey;
|
||||
enum wc_LmsRc ret = WC_LMS_RC_NONE;
|
||||
|
||||
if (private_key == NULL || key == NULL || len_private_key <= 0) {
|
||||
WOLFSSL_MSG("error: LmsReadPrivKey: invalid args");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key->state != WC_LMS_STATE_PARMSET && key->state != WC_LMS_STATE_OK) {
|
||||
/* The LmsKey is not ready for reading. */
|
||||
WOLFSSL_MSG("error: LmsReadPrivKey: LMS key not in readable state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key->read_private_key == NULL) {
|
||||
WOLFSSL_MSG("error: LmsReadPrivKey: LMS key read callback not set");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Use read callback that reads private key from non-volatile storage. */
|
||||
ret = key->read_private_key(private_key, len_private_key, key->context);
|
||||
|
||||
if (ret != WC_LMS_RC_READ_TO_MEMORY) {
|
||||
WOLFSSL_MSG("error: LmsKey read_private_key failed");
|
||||
WOLFSSL_MSG(wc_LmsKey_RcToStr(ret));
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm)
|
||||
{
|
||||
switch (lmsParm) {
|
||||
case WC_LMS_PARM_NONE:
|
||||
return "LMS_NONE";
|
||||
|
||||
case WC_LMS_PARM_L1_H15_W2:
|
||||
return "LMS/HSS L1_H15_W2";
|
||||
|
||||
case WC_LMS_PARM_L1_H15_W4:
|
||||
return "LMS/HSS L1_H15_W4";
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W2:
|
||||
return "LMS/HSS L2_H10_W2";
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W4:
|
||||
return "LMS/HSS L2_H10_W4";
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W8:
|
||||
return "LMS/HSS L2_H10_W8";
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W2:
|
||||
return "LMS/HSS L3_H5_W2";
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W4:
|
||||
return "LMS/HSS L3_H5_W4";
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W8:
|
||||
return "LMS/HSS L3_H5_W8";
|
||||
|
||||
case WC_LMS_PARM_L3_H10_W4:
|
||||
return "LMS/HSS L3_H10_W4";
|
||||
|
||||
case WC_LMS_PARM_L4_H5_W8:
|
||||
return "LMS/HSS L4_H5_W8";
|
||||
|
||||
default:
|
||||
WOLFSSL_MSG("error: invalid LMS parameter");
|
||||
break;
|
||||
}
|
||||
|
||||
return "LMS_INVALID";
|
||||
}
|
||||
|
||||
const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsEc)
|
||||
{
|
||||
switch (lmsEc) {
|
||||
case WC_LMS_RC_NONE:
|
||||
return "LMS_RC_NONE";
|
||||
|
||||
case WC_LMS_RC_BAD_ARG:
|
||||
return "LMS_RC_BAD_ARG";
|
||||
|
||||
case WC_LMS_RC_WRITE_FAIL:
|
||||
return "LMS_RC_WRITE_FAIL";
|
||||
|
||||
case WC_LMS_RC_READ_FAIL:
|
||||
return "LMS_RC_READ_FAIL";
|
||||
|
||||
case WC_LMS_RC_SAVED_TO_NV_MEMORY:
|
||||
return "LMS_RC_SAVED_TO_NV_MEMORY";
|
||||
|
||||
case WC_LMS_RC_READ_TO_MEMORY:
|
||||
return "LMS_RC_READ_TO_MEMORY";
|
||||
|
||||
default:
|
||||
WOLFSSL_MSG("error: invalid LMS error code");
|
||||
break;
|
||||
}
|
||||
|
||||
return "LMS_RC_INVALID";
|
||||
}
|
||||
|
||||
/* Init an LMS key.
|
||||
*
|
||||
* Call this before setting the parms of an LMS key.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_Init(LmsKey * key, void * heap, int devId)
|
||||
{
|
||||
if (key == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
(void) heap;
|
||||
(void) devId;
|
||||
|
||||
ForceZero(key, sizeof(LmsKey));
|
||||
|
||||
/* Set the max number of worker threads that hash-sigs can spawn. */
|
||||
hss_init_extra_info(&key->info);
|
||||
hss_extra_info_set_threads(&key->info, EXT_LMS_MAX_THREADS);
|
||||
|
||||
key->working_key = NULL;
|
||||
key->write_private_key = NULL;
|
||||
key->read_private_key = NULL;
|
||||
key->context = NULL;
|
||||
key->state = WC_LMS_STATE_INITED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the wc_LmsParm of an LMS key.
|
||||
*
|
||||
* Use this if you wish to set a key with a predefined parameter set,
|
||||
* such as WC_LMS_PARM_L2_H10_W8.
|
||||
*
|
||||
* Key must be inited before calling this.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_SetLmsParm(LmsKey * key, enum wc_LmsParm lmsParm)
|
||||
{
|
||||
if (key == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* If NONE is passed, default to the lowest predefined set. */
|
||||
switch (lmsParm) {
|
||||
case WC_LMS_PARM_NONE:
|
||||
case WC_LMS_PARM_L1_H15_W2:
|
||||
return wc_LmsKey_SetParameters(key, 1, 15, 2);
|
||||
|
||||
case WC_LMS_PARM_L1_H15_W4:
|
||||
return wc_LmsKey_SetParameters(key, 1, 15, 4);
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W2:
|
||||
return wc_LmsKey_SetParameters(key, 2, 10, 2);
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W4:
|
||||
return wc_LmsKey_SetParameters(key, 2, 10, 4);
|
||||
|
||||
case WC_LMS_PARM_L2_H10_W8:
|
||||
return wc_LmsKey_SetParameters(key, 2, 10, 8);
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W2:
|
||||
return wc_LmsKey_SetParameters(key, 3, 5, 2);
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W4:
|
||||
return wc_LmsKey_SetParameters(key, 3, 5, 4);
|
||||
|
||||
case WC_LMS_PARM_L3_H5_W8:
|
||||
return wc_LmsKey_SetParameters(key, 3, 5, 8);
|
||||
|
||||
case WC_LMS_PARM_L3_H10_W4:
|
||||
return wc_LmsKey_SetParameters(key, 3, 10, 4);
|
||||
|
||||
case WC_LMS_PARM_L4_H5_W8:
|
||||
return wc_LmsKey_SetParameters(key, 4, 5, 8);
|
||||
|
||||
default:
|
||||
WOLFSSL_MSG("error: invalid LMS parameter set");
|
||||
break;
|
||||
}
|
||||
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* Set the parameters of an LMS key.
|
||||
*
|
||||
* Use this if you wish to set specific parameters not found in the
|
||||
* wc_LmsParm predefined sets. See comments in lms.h for allowed
|
||||
* parameters.
|
||||
*
|
||||
* Key must be inited before calling this.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_SetParameters(LmsKey * key, int levels, int height,
|
||||
int winternitz)
|
||||
{
|
||||
int i = 0;
|
||||
param_set_t lm = LMS_SHA256_N32_H5;
|
||||
param_set_t ots = LMOTS_SHA256_N32_W1;
|
||||
|
||||
if (key == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (key->state != WC_LMS_STATE_INITED) {
|
||||
WOLFSSL_MSG("error: LmsKey needs init");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify inputs make sense.
|
||||
*
|
||||
* Note: there does not seem to be a define for min or
|
||||
* max Winternitz integer in hash-sigs lib or RFC8554. */
|
||||
|
||||
if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) {
|
||||
WOLFSSL_MSG("error: invalid level parameter");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (height < MIN_MERKLE_HEIGHT || height > MAX_MERKLE_HEIGHT) {
|
||||
WOLFSSL_MSG("error: invalid height parameter");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
switch (height) {
|
||||
case 5:
|
||||
lm = LMS_SHA256_N32_H5;
|
||||
break;
|
||||
case 10:
|
||||
lm = LMS_SHA256_N32_H10;
|
||||
break;
|
||||
case 15:
|
||||
lm = LMS_SHA256_N32_H15;
|
||||
break;
|
||||
case 20:
|
||||
lm = LMS_SHA256_N32_H20;
|
||||
break;
|
||||
case 25:
|
||||
lm = LMS_SHA256_N32_H25;
|
||||
break;
|
||||
default:
|
||||
WOLFSSL_MSG("error: invalid height parameter");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
switch (winternitz) {
|
||||
case 1:
|
||||
ots = LMOTS_SHA256_N32_W1;
|
||||
break;
|
||||
case 2:
|
||||
ots = LMOTS_SHA256_N32_W2;
|
||||
break;
|
||||
case 4:
|
||||
ots = LMOTS_SHA256_N32_W4;
|
||||
break;
|
||||
case 8:
|
||||
ots = LMOTS_SHA256_N32_W8;
|
||||
break;
|
||||
default:
|
||||
WOLFSSL_MSG("error: invalid winternitz parameter");
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
key->levels = levels;
|
||||
|
||||
for (i = 0; i < levels; ++i) {
|
||||
key->lm_type[i] = lm;
|
||||
key->lm_ots_type[i] = ots;
|
||||
}
|
||||
|
||||
/* Move the state to parms set.
|
||||
* Key is ready for MakeKey or Reload. */
|
||||
key->state = WC_LMS_STATE_PARMSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Frees the LMS key from memory.
|
||||
*
|
||||
* This does not affect the private key saved to non-volatile storage.
|
||||
* */
|
||||
void wc_LmsKey_Free(LmsKey* key)
|
||||
{
|
||||
if (key == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key->working_key != NULL) {
|
||||
hss_free_working_key(key->working_key);
|
||||
key->working_key = NULL;
|
||||
}
|
||||
|
||||
ForceZero(key, sizeof(LmsKey));
|
||||
|
||||
key->state = WC_LMS_STATE_FREED;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the write private key callback to the LMS key structure.
|
||||
*
|
||||
* The callback must be able to write/update the private key to
|
||||
* non-volatile storage.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb write_cb)
|
||||
{
|
||||
if (key == NULL || write_cb == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* Changing the write callback of an already working key is forbidden. */
|
||||
if (key->state == WC_LMS_STATE_OK) {
|
||||
WOLFSSL_MSG("error: wc_LmsKey_SetWriteCb: key in use");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->write_private_key = write_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the read private key callback to the LMS key structure.
|
||||
*
|
||||
* The callback must be able to read the private key from
|
||||
* non-volatile storage.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb read_cb)
|
||||
{
|
||||
if (key == NULL || read_cb == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* Changing the read callback of an already working key is forbidden. */
|
||||
if (key->state == WC_LMS_STATE_OK) {
|
||||
WOLFSSL_MSG("error: wc_LmsKey_SetReadCb: key in use");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->read_private_key = read_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets the context to be used by write and read callbacks.
|
||||
*
|
||||
* E.g. this could be a filename if the callbacks write/read to file.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_SetContext(LmsKey * key, void * context)
|
||||
{
|
||||
if (key == NULL || context == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* Setting context of an already working key is forbidden. */
|
||||
if (key->state == WC_LMS_STATE_OK) {
|
||||
WOLFSSL_MSG("error: wc_LmsKey_SetContext: key in use");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->context = context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make the LMS private/public key pair. The key must have its parameters
|
||||
* set before calling this.
|
||||
*
|
||||
* Write/read callbacks, and context data, must be set prior.
|
||||
* Key must have parameters set.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* */
|
||||
int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (key == NULL || rng == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (key->state != WC_LMS_STATE_PARMSET) {
|
||||
WOLFSSL_MSG("error: LmsKey not ready for generation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key->write_private_key == NULL || key->read_private_key == NULL) {
|
||||
WOLFSSL_MSG("error: LmsKey write/read callbacks are not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key->context == NULL) {
|
||||
WOLFSSL_MSG("error: LmsKey context is not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LmsRng = rng;
|
||||
|
||||
/* TODO: The hash-sigs lib allows you to save variable length auxiliary
|
||||
* data, which can be used to speed up key reloading when signing. The
|
||||
* aux data can be 300B - 1KB in size.
|
||||
*
|
||||
* Not implemented at the moment.
|
||||
*
|
||||
* key->aux_data_len = hss_get_aux_data_len(AUX_DATA_MAX_LEN, key->levels,
|
||||
* key->lm_type,
|
||||
* key->lm_ots_type);
|
||||
*
|
||||
* key->aux_data = XMALLOC(key->aux_data_len, NULL,
|
||||
* DYNAMIC_TYPE_TMP_BUFFER);
|
||||
*/
|
||||
|
||||
/* First generate the private key using the parameters and callbacks.
|
||||
* If successful, private key will be saved to non-volatile storage,
|
||||
* and the public key will be in memory. */
|
||||
result = hss_generate_private_key(LmsGenerateRand, key->levels,
|
||||
key->lm_type, key->lm_ots_type,
|
||||
LmsWritePrivKey, key,
|
||||
key->pub, sizeof(key->pub),
|
||||
NULL, 0, &key->info);
|
||||
|
||||
if (!result) {
|
||||
WOLFSSL_MSG("error: hss_generate_private_key failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Once generated, now we must load the private key so we have
|
||||
* an hss working key for signing operations. */
|
||||
key->working_key = hss_load_private_key(LmsReadPrivKey, key,
|
||||
0, NULL, 0, &key->info);
|
||||
|
||||
if (key->working_key == NULL) {
|
||||
WOLFSSL_MSG("error: hss_load_private_key failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This should not happen, but check just in case. */
|
||||
if (wc_LmsKey_SigsLeft(key) == 0) {
|
||||
WOLFSSL_MSG("error: generated LMS key signatures exhausted");
|
||||
key->state = WC_LMS_STATE_NOSIGS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->state = WC_LMS_STATE_OK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload a key that has been prepared with the appropriate parms and
|
||||
* data. Use this if you wish to resume signing with an existing key.
|
||||
*
|
||||
* Write/read callbacks, and context data, must be set prior.
|
||||
* Key must have parameters set.
|
||||
*
|
||||
* Returns 0 on success. */
|
||||
int wc_LmsKey_Reload(LmsKey * key)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (key == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (key->state != WC_LMS_STATE_PARMSET) {
|
||||
WOLFSSL_MSG("error: LmsKey not ready for reload");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key->write_private_key == NULL || key->read_private_key == NULL) {
|
||||
WOLFSSL_MSG("error: LmsKey write/read callbacks are not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key->context == NULL) {
|
||||
WOLFSSL_MSG("error: LmsKey context is not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->working_key = hss_load_private_key(LmsReadPrivKey, key,
|
||||
0, NULL, 0, &key->info);
|
||||
|
||||
if (key->working_key == NULL) {
|
||||
WOLFSSL_MSG("error: hss_load_private_key failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = hss_get_parameter_set(&key->levels, key->lm_type,
|
||||
key->lm_ots_type, LmsReadPrivKey, key);
|
||||
|
||||
if (!result) {
|
||||
WOLFSSL_MSG("error: hss_get_parameter_set failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
hss_free_working_key(key->working_key);
|
||||
key->working_key = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Double check the key actually has signatures left. */
|
||||
if (wc_LmsKey_SigsLeft(key) == 0) {
|
||||
WOLFSSL_MSG("error: reloaded LMS key signatures exhausted");
|
||||
key->state = WC_LMS_STATE_NOSIGS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
key->state = WC_LMS_STATE_OK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given a levels, height, winternitz parameter set, determine
|
||||
* the private key length */
|
||||
int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len)
|
||||
{
|
||||
if (key == NULL || len == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
*len = (word32) hss_get_private_key_len(key->levels, key->lm_type,
|
||||
key->lm_ots_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given a levels, height, winternitz parameter set, determine
|
||||
* the public key length */
|
||||
int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len)
|
||||
{
|
||||
if (key == NULL || len == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
*len = (word32) hss_get_public_key_len(key->levels, key->lm_type,
|
||||
key->lm_ots_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Export a generated public key and parameter set from one LmsKey
|
||||
* to another. Use this to prepare a signature verification LmsKey
|
||||
* that is pub only.
|
||||
*
|
||||
* Though the public key is all that is used to verify signatures,
|
||||
* the parameter set is needed to calculate the signature length
|
||||
* before hand. */
|
||||
int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc)
|
||||
{
|
||||
if (keyDst == NULL || keySrc == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
ForceZero(keyDst, sizeof(LmsKey));
|
||||
|
||||
XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
|
||||
XMEMCPY(keyDst->lm_type, keySrc->lm_type, sizeof(keySrc->lm_type));
|
||||
XMEMCPY(keyDst->lm_ots_type, keySrc->lm_ots_type,
|
||||
sizeof(keySrc->lm_ots_type));
|
||||
|
||||
keyDst->levels = keySrc->levels;
|
||||
|
||||
/* Mark this key as verify only, to prevent misuse. */
|
||||
keyDst->state = WC_LMS_STATE_VERIFYONLY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given a levels, height, winternitz parameter set, determine
|
||||
* the signature length.
|
||||
*
|
||||
* Call this before wc_LmsKey_Sign so you know the length of
|
||||
* the required signature buffer. */
|
||||
int wc_LmsKey_GetSigLen(LmsKey * key, word32 * len)
|
||||
{
|
||||
if (key == NULL || len == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
*len = (word32) hss_get_signature_len(key->levels, key->lm_type,
|
||||
key->lm_ots_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_LmsKey_Sign(LmsKey* key, byte * sig, word32 * sigSz, const byte * msg,
|
||||
int msgSz)
|
||||
{
|
||||
bool result = true;
|
||||
size_t len = 0;
|
||||
|
||||
if (key == NULL || sig == NULL || sigSz == NULL || msg == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (msgSz <= 0) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (key->state == WC_LMS_STATE_NOSIGS) {
|
||||
WOLFSSL_MSG("error: LMS signatures exhausted");
|
||||
return -1;
|
||||
}
|
||||
else if (key->state != WC_LMS_STATE_OK) {
|
||||
/* The key had an error the last time it was used, and we
|
||||
* can't guarantee its state. */
|
||||
WOLFSSL_MSG("error: can't sign, LMS key not in good state");
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = hss_get_signature_len(key->levels, key->lm_type, key->lm_ots_type);
|
||||
|
||||
if (len == 0) {
|
||||
/* Key parameters are invalid. */
|
||||
WOLFSSL_MSG("error: hss_get_signature_len failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = hss_generate_signature(key->working_key, LmsWritePrivKey,
|
||||
key, (const void *) msg, msgSz,
|
||||
sig, len, &key->info);
|
||||
|
||||
if (!result) {
|
||||
if (wc_LmsKey_SigsLeft(key) == 0) {
|
||||
WOLFSSL_MSG("error: LMS signatures exhausted");
|
||||
key->state = WC_LMS_STATE_NOSIGS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
WOLFSSL_MSG("error: hss_generate_signature failed");
|
||||
key->state = WC_LMS_STATE_BAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sigSz = (word32) len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz,
|
||||
const byte * msg, int msgSz)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (key == NULL || sig == NULL || msg == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
result = hss_validate_signature(key->pub, (const void *) msg, msgSz, sig,
|
||||
sigSz, &key->info);
|
||||
|
||||
if (!result) {
|
||||
WOLFSSL_MSG("error: hss_validate_signature failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Returns 1 if there are signatures remaining.
|
||||
* Returns 0 if available signatures are exhausted.
|
||||
*
|
||||
* Note: the number of remaining signatures is hidden behind an opaque
|
||||
* pointer in the hash-sigs lib. We could add a counter here that is
|
||||
* decremented on every signature. The number of available signatures
|
||||
* grows as
|
||||
* N = 2 ** (levels * height)
|
||||
* so it would need to be a big integer. */
|
||||
int wc_LmsKey_SigsLeft(LmsKey * key)
|
||||
{
|
||||
if (key == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (hss_extra_info_test_last_signature(&key->info)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
26
wolfcrypt/src/wc_lms.c
Normal file
26
wolfcrypt/src/wc_lms.c
Normal file
@ -0,0 +1,26 @@
|
||||
/* wc_lms.c
|
||||
*
|
||||
* Copyright (C) 2006-2023 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 <wolfssl/wolfcrypt/settings.h>
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#error "Contact wolfSSL to get the implementation of this file"
|
||||
#endif
|
@ -284,6 +284,12 @@ const byte const_byte_array[] = "A+Gd\0\0\0";
|
||||
#include <wolfssl/wolfcrypt/ext_kyber.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#include <wolfssl/wolfcrypt/lms.h>
|
||||
#ifdef HAVE_LIBLMS
|
||||
#include <wolfssl/wolfcrypt/ext_lms.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_HAVE_ECCSI
|
||||
#include <wolfssl/wolfcrypt/eccsi.h>
|
||||
#endif
|
||||
@ -555,6 +561,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void);
|
||||
#ifdef WOLFSSL_HAVE_KYBER
|
||||
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void);
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
WOLFSSL_TEST_SUBROUTINE int lms_test(void);
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_HAVE_ECCSI
|
||||
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t eccsi_test(void);
|
||||
#endif
|
||||
@ -1580,6 +1589,13 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
|
||||
TEST_PASS("KYBER test passed!\n");
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
if ( (ret = lms_test()) != 0)
|
||||
TEST_FAIL("LMS test failed!\n", ret);
|
||||
else
|
||||
TEST_PASS("LMS test passed!\n");
|
||||
#endif
|
||||
|
||||
#ifdef WOLFCRYPT_HAVE_ECCSI
|
||||
if ( (ret = eccsi_test()) != 0)
|
||||
TEST_FAIL("ECCSI test failed!\n", ret);
|
||||
@ -34908,6 +34924,144 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void)
|
||||
}
|
||||
#endif /* WOLFSSL_HAVE_KYBER */
|
||||
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
static int lms_write_key_mem(const byte * priv, word32 privSz, void *context)
|
||||
{
|
||||
/* WARNING: THIS IS AN INSECURE WRITE CALLBACK THAT SHOULD ONLY
|
||||
* BE USED FOR TESTING PURPOSES! Production applications should
|
||||
* write only to non-volatile storage. */
|
||||
XMEMCPY(context, priv, privSz);
|
||||
return WC_LMS_RC_SAVED_TO_NV_MEMORY;
|
||||
}
|
||||
|
||||
static int lms_read_key_mem(byte * priv, word32 privSz, void *context)
|
||||
{
|
||||
/* WARNING: THIS IS AN INSECURE READ CALLBACK THAT SHOULD ONLY
|
||||
* BE USED FOR TESTING PURPOSES! */
|
||||
XMEMCPY(priv, context, privSz);
|
||||
return WC_LMS_RC_READ_TO_MEMORY;
|
||||
}
|
||||
|
||||
/* LMS signature sizes are a function of their parameters. This
|
||||
* test has a signature of 8688 bytes. */
|
||||
#define WC_TEST_LMS_SIG_LEN (8688)
|
||||
|
||||
WOLFSSL_TEST_SUBROUTINE int lms_test(void)
|
||||
{
|
||||
int ret;
|
||||
int sigsLeft = 0;
|
||||
LmsKey signingKey;
|
||||
LmsKey verifyKey;
|
||||
WC_RNG rng;
|
||||
word32 sigSz = 0;
|
||||
const char * msg = "LMS HSS post quantum signature test";
|
||||
word32 msgSz = (word32) XSTRLEN(msg);
|
||||
unsigned char priv[HSS_MAX_PRIVATE_KEY_LEN];
|
||||
unsigned char old_priv[HSS_MAX_PRIVATE_KEY_LEN];
|
||||
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
|
||||
byte * sig = XMALLOC(WC_TEST_LMS_SIG_LEN, HEAP_HINT,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (sig == NULL) {
|
||||
return WC_TEST_RET_ENC_ERRNO;
|
||||
}
|
||||
#else
|
||||
byte sig[WC_TEST_LMS_SIG_LEN];
|
||||
#endif
|
||||
|
||||
XMEMSET(priv, 0, sizeof(priv));
|
||||
XMEMSET(old_priv, 0, sizeof(old_priv));
|
||||
|
||||
#ifndef HAVE_FIPS
|
||||
ret = wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID);
|
||||
#else
|
||||
ret = wc_InitRng(&rng);
|
||||
#endif
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
/* This test:
|
||||
* levels: 1
|
||||
* height: 5
|
||||
* winternitz: 1
|
||||
*
|
||||
* max sigs: 2 ** (1 * 5) = 32
|
||||
* signature length: 8688
|
||||
*/
|
||||
|
||||
ret = wc_LmsKey_Init(&signingKey, NULL, INVALID_DEVID);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_Init(&verifyKey, NULL, INVALID_DEVID);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_SetParameters(&signingKey, 1, 5, 1);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_SetWriteCb(&signingKey, lms_write_key_mem);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_SetReadCb(&signingKey, lms_read_key_mem);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_SetContext(&signingKey, (void *) priv);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_MakeKey(&signingKey, &rng);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
XMEMCPY(old_priv, priv, sizeof(priv));
|
||||
|
||||
ret = wc_LmsKey_ExportPub(&verifyKey, &signingKey);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
ret = wc_LmsKey_GetSigLen(&verifyKey, &sigSz);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
|
||||
|
||||
if (sigSz != WC_TEST_LMS_SIG_LEN) {
|
||||
printf("error: got %d, expected %d\n", sigSz, WC_TEST_LMS_SIG_LEN);
|
||||
return WC_TEST_RET_ENC_EC(sigSz);
|
||||
}
|
||||
|
||||
/* 2 ** 5 should be the max number of signatures */
|
||||
for (size_t i = 0; i < 32; ++i) {
|
||||
/* We should have remaining signstures. */
|
||||
sigsLeft = wc_LmsKey_SigsLeft(&signingKey);
|
||||
if (sigsLeft == 0) {
|
||||
return WC_TEST_RET_ENC_EC(sigsLeft);
|
||||
}
|
||||
|
||||
/* Sign with key. The private key will be updated on every signature. */
|
||||
ret = wc_LmsKey_Sign(&signingKey, sig, &sigSz, (byte *) msg, msgSz);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_I(i); }
|
||||
|
||||
/* The updated private key should not match the old one. */
|
||||
if (XMEMCMP(old_priv, priv, sizeof(priv)) == 0) {
|
||||
printf("error: current priv key should not match old: %zu\n", i);
|
||||
return WC_TEST_RET_ENC_I(i);
|
||||
}
|
||||
|
||||
XMEMCPY(old_priv, priv, sizeof(priv));
|
||||
|
||||
ret = wc_LmsKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, msgSz);
|
||||
if (ret != 0) { return WC_TEST_RET_ENC_I(i); }
|
||||
}
|
||||
|
||||
/* This should be the last signature. */
|
||||
sigsLeft = wc_LmsKey_SigsLeft(&signingKey);
|
||||
if (sigsLeft != 0) {
|
||||
return WC_TEST_RET_ENC_EC(sigsLeft);
|
||||
}
|
||||
|
||||
wc_LmsKey_Free(&signingKey);
|
||||
wc_LmsKey_Free(&verifyKey);
|
||||
|
||||
wc_FreeRng(&rng);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
|
||||
static const int fiducial3 = WC_TEST_RET_LN; /* source code reference point --
|
||||
* see print_fiducials() below.
|
||||
*/
|
||||
|
63
wolfssl/wolfcrypt/ext_lms.h
Normal file
63
wolfssl/wolfcrypt/ext_lms.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* ext_lms.h
|
||||
*
|
||||
* Copyright (C) 2006-2023 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_LMS_H
|
||||
#define EXT_LMS_H
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
#include <wolfssl/wolfcrypt/lms.h>
|
||||
|
||||
#if !defined(HAVE_LIBLMS)
|
||||
#error "This code requires liblms"
|
||||
#endif
|
||||
|
||||
/* hash-sigs LMS HSS includes */
|
||||
#include <hss.h>
|
||||
|
||||
#if defined(WOLFSSL_WC_LMS)
|
||||
#error "This code is incompatible with wolfCrypt's implementation of LMS."
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The hash-sigs LMS lib supports from MIN_HSS_LEVELS to MAX_HSS_LEVELS
|
||||
* number of levels of Merkle trees. It allows for the tree height and
|
||||
* winternitz parameter to be unique per level.
|
||||
*/
|
||||
|
||||
/* hss structs */
|
||||
typedef struct hss_working_key hss_working_key;
|
||||
typedef struct hss_extra_info hss_extra_info;
|
||||
|
||||
struct LmsKey {
|
||||
unsigned levels; /* Number of tree levels. */
|
||||
param_set_t lm_type[MAX_HSS_LEVELS]; /* Height parm per level. */
|
||||
param_set_t lm_ots_type[MAX_HSS_LEVELS]; /* Winternitz parm per level. */
|
||||
unsigned char pub[HSS_MAX_PUBLIC_KEY_LEN];
|
||||
hss_working_key * working_key;
|
||||
write_private_key_cb write_private_key; /* Callback to write/update key. */
|
||||
read_private_key_cb read_private_key; /* Callback to read key. */
|
||||
void * context; /* Context arg passed to callbacks. */
|
||||
hss_extra_info info;
|
||||
enum wc_LmsState state;
|
||||
};
|
||||
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#endif /* EXT_LMS_H */
|
@ -78,7 +78,10 @@ nobase_include_HEADERS+= \
|
||||
wolfssl/wolfcrypt/ext_kyber.h \
|
||||
wolfssl/wolfcrypt/sm2.h \
|
||||
wolfssl/wolfcrypt/sm3.h \
|
||||
wolfssl/wolfcrypt/sm4.h
|
||||
wolfssl/wolfcrypt/sm4.h \
|
||||
wolfssl/wolfcrypt/lms.h \
|
||||
wolfssl/wolfcrypt/wc_lms.h \
|
||||
wolfssl/wolfcrypt/ext_lms.h
|
||||
|
||||
noinst_HEADERS+= \
|
||||
wolfssl/wolfcrypt/port/aria/aria-crypt.h \
|
||||
|
136
wolfssl/wolfcrypt/lms.h
Normal file
136
wolfssl/wolfcrypt/lms.h
Normal file
@ -0,0 +1,136 @@
|
||||
/* lms.h
|
||||
*
|
||||
* Copyright (C) 2006-2023 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
|
||||
*/
|
||||
|
||||
/*!
|
||||
\file wolfssl/wolfcrypt/lms.h
|
||||
*/
|
||||
|
||||
#ifndef WOLF_CRYPT_LMS_H
|
||||
#define WOLF_CRYPT_LMS_H
|
||||
|
||||
#include <wolfssl/wolfcrypt/types.h>
|
||||
#include <wolfssl/wolfcrypt/random.h>
|
||||
|
||||
#ifdef WOLFSSL_HAVE_LMS
|
||||
|
||||
typedef struct LmsKey LmsKey;
|
||||
|
||||
/* Private key write and read callbacks. */
|
||||
typedef int (*write_private_key_cb)(const byte * priv, word32 privSz, void *context);
|
||||
typedef int (*read_private_key_cb)(byte * priv, word32 privSz, void *context);
|
||||
|
||||
/* Return codes returned by private key callbacks. */
|
||||
enum wc_LmsRc {
|
||||
WC_LMS_RC_NONE,
|
||||
WC_LMS_RC_BAD_ARG, /* Bad arg in read or write callback. */
|
||||
WC_LMS_RC_WRITE_FAIL, /* Write or update private key failed. */
|
||||
WC_LMS_RC_READ_FAIL, /* Read private key failed. */
|
||||
WC_LMS_RC_SAVED_TO_NV_MEMORY, /* Wrote private key to nonvolatile storage. */
|
||||
WC_LMS_RC_READ_TO_MEMORY /* Read private key from storage. */
|
||||
};
|
||||
|
||||
/* LMS/HSS signatures are defined by 3 parameters:
|
||||
* levels: number of levels of Merkle trees.
|
||||
* height: height of an individual Merkle tree.
|
||||
* winternitz: number of bits from hash used in a Winternitz chain.
|
||||
*
|
||||
* The acceptable parameter values are those in RFC8554:
|
||||
* levels = {1..8}
|
||||
* height = {5, 10, 15, 20, 25}
|
||||
* winternitz = {1, 2, 4, 8}
|
||||
*
|
||||
* The number of available signatures is:
|
||||
* N = 2 ** (levels * height)
|
||||
*
|
||||
* Signature sizes are determined by levels and winternitz
|
||||
* parameters primarily, and height to a lesser extent:
|
||||
* - Larger levels values increase signature size significantly.
|
||||
* - Larger height values increase signature size moderately.
|
||||
* - Larger winternitz values will reduce the signature size, at
|
||||
* the expense of longer key generation and sign/verify times.
|
||||
*
|
||||
* Key generation time is strongly determined by the height of
|
||||
* the first level tree. A 3 level, 5 height tree is much faster
|
||||
* than 1 level, 15 height at initial key gen, even if the number
|
||||
* of available signatures is the same.
|
||||
* */
|
||||
|
||||
/* Predefined LMS/HSS parameter sets for convenience.
|
||||
*
|
||||
* Not predefining a set with Winternitz=1, because the signatures
|
||||
* will be large. */
|
||||
enum wc_LmsParm {
|
||||
WC_LMS_PARM_NONE = 0,
|
||||
WC_LMS_PARM_L1_H15_W2 = 1, /* 1 level Merkle tree of 15 height. */
|
||||
WC_LMS_PARM_L1_H15_W4 = 2,
|
||||
WC_LMS_PARM_L2_H10_W2 = 3, /* 2 level Merkle tree of 10 height. */
|
||||
WC_LMS_PARM_L2_H10_W4 = 4,
|
||||
WC_LMS_PARM_L2_H10_W8 = 5,
|
||||
WC_LMS_PARM_L3_H5_W2 = 6, /* 3 level Merkle tree of 5 height. */
|
||||
WC_LMS_PARM_L3_H5_W4 = 7,
|
||||
WC_LMS_PARM_L3_H5_W8 = 8,
|
||||
WC_LMS_PARM_L3_H10_W4 = 9, /* 3 level Merkle tree of 10 height. */
|
||||
WC_LMS_PARM_L4_H5_W8 = 10, /* 4 level Merkle tree of 5 height. */
|
||||
};
|
||||
|
||||
/* enum wc_LmsState is to help track the state of an LMS/HSS Key. */
|
||||
enum wc_LmsState {
|
||||
WC_LMS_STATE_FREED, /* Key has been freed from memory. */
|
||||
WC_LMS_STATE_INITED, /* Key has been inited, ready to set parms.*/
|
||||
WC_LMS_STATE_PARMSET, /* Parms are set, ready to MakeKey or Reload. */
|
||||
WC_LMS_STATE_OK, /* Able to sign signatures and verify. */
|
||||
WC_LMS_STATE_VERIFYONLY, /* A public only LmsKey. */
|
||||
WC_LMS_STATE_BAD, /* Can't guarantee key's state. */
|
||||
WC_LMS_STATE_NOSIGS /* Signatures exhausted. */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
WOLFSSL_API int wc_LmsKey_Init(LmsKey * key, void * heap, int devId);
|
||||
WOLFSSL_API int wc_LmsKey_SetLmsParm(LmsKey * key, enum wc_LmsParm lmsParm);
|
||||
WOLFSSL_API int wc_LmsKey_SetParameters(LmsKey * key, int levels,
|
||||
int height, int winternitz);
|
||||
WOLFSSL_API int wc_LmsKey_SetWriteCb(LmsKey * key,
|
||||
write_private_key_cb write_cb);
|
||||
WOLFSSL_API int wc_LmsKey_SetReadCb(LmsKey * key,
|
||||
read_private_key_cb read_cb);
|
||||
WOLFSSL_API int wc_LmsKey_SetContext(LmsKey * key, void * context);
|
||||
WOLFSSL_API void wc_LmsKey_Free(LmsKey * key);
|
||||
WOLFSSL_API int wc_LmsKey_MakeKey(LmsKey * key, WC_RNG * rng);
|
||||
WOLFSSL_API int wc_LmsKey_Reload(LmsKey * key);
|
||||
WOLFSSL_API int wc_LmsKey_GetSigLen(LmsKey * key, word32 * len);
|
||||
WOLFSSL_API int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len);
|
||||
WOLFSSL_API int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len);
|
||||
WOLFSSL_API int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc);
|
||||
WOLFSSL_API int wc_LmsKey_Sign(LmsKey * key, byte * sig, word32 * sigSz,
|
||||
const byte * msg, int msgSz);
|
||||
WOLFSSL_API int wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz,
|
||||
const byte * msg, int msgSz);
|
||||
WOLFSSL_API int wc_LmsKey_SigsLeft(LmsKey * key);
|
||||
WOLFSSL_API const char * wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm);
|
||||
WOLFSSL_API const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsRc);
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* WOLFSSL_HAVE_LMS */
|
||||
#endif /* WOLF_CRYPT_LMS_H */
|
23
wolfssl/wolfcrypt/wc_lms.h
Normal file
23
wolfssl/wolfcrypt/wc_lms.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* wc_lms.h
|
||||
*
|
||||
* Copyright (C) 2006-2023 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
|
||||
*/
|
||||
|
||||
#error "Contact wolfSSL to get the implementation of this file"
|
||||
|
Loading…
x
Reference in New Issue
Block a user