diff --git a/.gitignore b/.gitignore index 57c2644de..9ca0168ad 100644 --- a/.gitignore +++ b/.gitignore @@ -267,6 +267,12 @@ pq/stateful_hash_sig/xmss_example pq/stateful_hash_sig/xmss_example.key pq/stateful_hash_sig/lms_example pq/stateful_hash_sig/lms_example.key +pq/stateful_hash_sig/lms-kg +pq/stateful_hash_sig/lms-sign +pq/stateful_hash_sig/lms-verify +pq/stateful_hash_sig/lms_key.bin +pq/stateful_hash_sig/lms_pubkey.bin +pq/stateful_hash_sig/*.sig # PQ ML-DSA pq/ml_dsa/ml_dsa_test diff --git a/pq/stateful_hash_sig/Makefile b/pq/stateful_hash_sig/Makefile index e6271277c..839c18310 100644 --- a/pq/stateful_hash_sig/Makefile +++ b/pq/stateful_hash_sig/Makefile @@ -44,6 +44,23 @@ lms_example: lms_example.c # If building with ext_lms (--enable-lms --with-liblms=): # $(CC) -o $@ $< $(CFLAGS) -I$(HSS_INC) $(LIBS) $(WOLF_STATIC_LIB) $(HSS_LIB) +lms-kg: lms-kg.c +# If building with wc_lms (--enable-lms): + $(CC) -o $@ $< $(CFLAGS) -DWOLFSSL_WC_LMS $(LIBS) $(WOLF_DYN_LIB) +# If building with ext_lms (--enable-lms --with-liblms=): +# $(CC) -o $@ $< $(CFLAGS) -I$(HSS_INC) $(LIBS) $(WOLF_STATIC_LIB) $(HSS_LIB) + +lms-sign: lms-sign.c +# If building with wc_lms (--enable-lms): + $(CC) -o $@ $< $(CFLAGS) -DWOLFSSL_WC_LMS $(LIBS) $(WOLF_DYN_LIB) +# If building with ext_lms (--enable-lms --with-liblms=): +# $(CC) -o $@ $< $(CFLAGS) -I$(HSS_INC) $(LIBS) $(WOLF_STATIC_LIB) $(HSS_LIB) + +lms-verify: lms-verify.c +# If building with wc_lms (--enable-lms): + $(CC) -o $@ $< $(CFLAGS) -DWOLFSSL_WC_LMS $(LIBS) $(WOLF_DYN_LIB) +# If building with ext_lms (--enable-lms --with-liblms=): +# $(CC) -o $@ $< $(CFLAGS) -I$(HSS_INC) $(LIBS) $(WOLF_STATIC_LIB) $(HSS_LIB) xmss_example: xmss_example.c # If building with wc_xmss (--enable-xmss): $(CC) -o $@ $< $(CFLAGS) -DWOLFSSL_WC_XMSS $(LIBS) $(WOLF_DYN_LIB) diff --git a/pq/stateful_hash_sig/README.md b/pq/stateful_hash_sig/README.md index a5612c1e3..827ce1f90 100644 --- a/pq/stateful_hash_sig/README.md +++ b/pq/stateful_hash_sig/README.md @@ -52,6 +52,83 @@ examples: description: ... ``` + +## Generate an LMS private/public key pair + +This example generates an LMS public/private key pair and stores them to file. + +```sh +$./lms-kg +Levels: 1, Height: 10, Winternitz: 4 + Writing to file: lms_key.bin + Writing to file: lms_pubkey.bin + +$./lms-kg --params 54 +Using parameters: LMS/HSS_SHA256/192_L1_H20_W4 + Writing to file: lms_key.bin + Writing to file: lms_pubkey.bin +``` + +## Sign files with LMS private key + +This example uses an LMS private key to sign multiple files. + +```sh +$./lms-sign Makefile README.md +Levels: 1, Height: 10, Winternitz: 4 + Reading from file: lms_key.bin + Read 56 bytes +Ready to sign + Reading from file: Makefile + Read 2455 bytes + Writing to file: lms_key.bin + Writing to file: Makefile.sig + Reading from file: README.md + Read 7756 bytes + Writing to file: lms_key.bin + Writing to file: README.md.sig + +$./lms-sign --params 54 Makefile README.md +Using parameters: LMS/HSS_SHA256/192_L1_H20_W4 + Reading from file: lms_key.bin + Read 56 bytes +Ready to sign + Reading from file: Makefile + Read 2455 bytes + Writing to file: lms_key.bin + Writing to file: Makefile.sig + Reading from file: README.md + Read 7756 bytes + Writing to file: lms_key.bin + Writing to file: README.md.sig +``` + +## Verify file with LMS public key + +This example uses an LMS public key to verify a file against its signature. + +```sh +$./lms-verify README.md +Levels: 1, Height: 10, Winternitz: 4 + Reading from file: lms_pubkey.bin + Read 52 bytes + Reading from file: README.md + Read 7756 bytes + Reading from file: README.md.sig + Read 1744 bytes +Verification succeeded + +$./lms-verify --params 54 README.md +Using parameters: LMS/HSS_SHA256/192_L1_H20_W4 + Reading from file: lms_pubkey.bin + Read 52 bytes + Reading from file: README.md + Read 7756 bytes + Reading from file: README.md.sig + Read 1744 bytes +Verification succeeded +``` + ## Signing and Verifying a Message with XMSS/XMSS^MT To see the help and usage, run the program without options: diff --git a/pq/stateful_hash_sig/lms-kg.c b/pq/stateful_hash_sig/lms-kg.c new file mode 100644 index 000000000..4adc637ee --- /dev/null +++ b/pq/stateful_hash_sig/lms-kg.c @@ -0,0 +1,292 @@ +/* lms-kg.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Example to demonstrate LMS Key Generation */ +/* + * ./configure --enable-lms && make && sudo make install + * gcc -lwolfssl -o lms-kg lms-kg.c + */ + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#ifdef WOLFSSL_WC_LMS +#include +#else +#include +#endif +#include +#include + +#include + +#ifdef WOLFSSL_HAVE_LMS + +void print_wolfssl_error(const char* msg, int err) +{ +#ifndef NO_ERROR_STRINGS + printf("%s: %s (%d)\n", msg, wc_GetErrorString(err), err); +#else + printf("%s: %d\n", msg, err); +#endif +} + +static int write_file(const char* filename, const unsigned char* data, + word32 sz) +{ + FILE* f; + int err = 1; + + printf(" Writing to file: %s\n", filename); + + /* Open file to put LMS private into. */ + f = fopen(filename, "wb"); + if (f == NULL) { + printf(" unable to write to file\n"); + goto write_file_end; + } + + /* Write out LMS private key. */ + fwrite(data, 1, sz, f); + fclose(f); + + err = 0; +write_file_end: + return err; +} + +static int lms_write_key_file(const byte* priv, word32 privSz, void *context) +{ + int ret = WC_LMS_RC_WRITE_FAIL; + const char* filename = (const char*)context; + + /* Write the private key to file. */ + if (write_file(filename, priv, privSz) == 0) + ret = WC_LMS_RC_SAVED_TO_NV_MEMORY; + + return ret; +} + +static int lms_export_public_key(LmsKey* key, const char* filename) +{ + int ret; + unsigned char* pub; + word32 pubSz; + + /* Get the public key length. */ + ret = wc_LmsKey_GetPubLen(key, &pubSz); + if (ret != 0) { + print_wolfssl_error("Failed to get public key size", ret); + goto exit_lms_export_public_key; + } + + /* Allocate memory for public key to be exported into. */ + pub = malloc(pubSz); + if (pub == NULL) { + ret = MEMORY_E; + printf("Failed to allocate memory: %d bytes\n", pubSz); + goto exit_lms_export_public_key; + } + + /* Export public key. */ + ret = wc_LmsKey_ExportPubRaw(key, pub, &pubSz); + if (ret != 0) { + print_wolfssl_error("Failed to export public key", ret); + goto exit_lms_export_public_key; + } + + /* Write public key to file. */ + ret = write_file(filename, pub, pubSz); + +exit_lms_export_public_key: + if (pub != NULL) + free(pub); + + return ret; +} + +int main(int argc, char* argv[]) +{ + int ret; + WC_RNG rng[1]; + LmsKey key[1]; + const char* filename = "lms_key.bin"; + const char* pubKeyFilename = "lms_pubkey.bin"; + int levels = 1; + int height = 10; + int winternitz = 4; + int params = -1; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + XMEMSET(rng, 0, sizeof(rng)); + XMEMSET(key, 0, sizeof(key)); + + argc--; + argv++; + while (argc > 0) { + /* Number of levels of trees. */ + if (strcmp(argv[0], "--levels") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No level value supplied\n"); + goto exit_lms_kg; + } + levels = atoi(argv[0]); + if ((levels < 1) || (levels > 4)) { + ret = -1; + printf("Invalid levels (1-4): %d\n", levels); + } + } + /* Height of each tree. */ + else if (strcmp(argv[0], "--height") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No height value supplied\n"); + goto exit_lms_kg; + } + height = atoi(argv[0]); + if ((height != 5) && (height != 10) && (height != 15) && + (height != 20)) { + ret = -1; + printf("Invalid height (5, 10, 15, 20): %d\n", height); + } + } + /* Winternitz value. */ + else if (strcmp(argv[0], "--winternitz") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No winternitz value supplied\n"); + goto exit_lms_kg; + } + winternitz = atoi(argv[0]); + if ((winternitz != 1) && (winternitz != 2) && (winternitz != 4) && + (winternitz != 8)) { + ret = -1; + printf("Invalid height (1, 2, 4, 8): %d\n", winternitz); + } + } + /* Parameters id. */ + else if (strcmp(argv[0], "--params") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No params value supplied\n"); + goto exit_lms_kg; + } + params = atoi(argv[0]); + if ((params < 1) || (params > 60)) { + ret = -1; + printf("Invalid params (1-60): %d\n", params); + goto exit_lms_kg; + } + } + else { + printf("Unrecognized option: %s\n", argv[0]); + goto exit_lms_kg; + } + + argc--; + argv++; + } + + /* Initialize random number generator for use in making a key. */ + ret = wc_InitRng(rng); + if (ret != 0) { + print_wolfssl_error("Failed to initialize random", ret); + goto exit_lms_kg; + } + + /* Initialize the LMS key. */ + ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID); + if (ret != 0) { + print_wolfssl_error("Failed to initialize LMS key", ret); + goto exit_lms_kg; + } + + /* Set parameters to use. */ + if (params == -1) { + printf("Levels: %d, Height: %d, Winternitz: %d\n", levels, height, + winternitz); + ret = wc_LmsKey_SetParameters(key, levels, height, winternitz); + } + else { + printf("Using parameters: %s\n", wc_LmsKey_ParmToStr(params)); + ret = wc_LmsKey_SetLmsParm(key, params); + } + if (ret != 0) { + print_wolfssl_error("Parameters for LMS not valid", ret); + goto exit_lms_kg; + } + + /* Set writing callback for generated private key. */ + ret = wc_LmsKey_SetWriteCb(key, lms_write_key_file); + if (ret != 0) { + print_wolfssl_error("Could not set write callback", ret); + goto exit_lms_kg; + } + + /* Set write callback context - the filename to write private key to. */ + ret = wc_LmsKey_SetContext(key, (void*)filename); + if (ret != 0) { + print_wolfssl_error("Could not set context for write callback", ret); + goto exit_lms_kg; + } + + /* Make the private/public key pair and store private key. */ + ret = wc_LmsKey_MakeKey(key, rng); + if (ret != 0) { + print_wolfssl_error("Failed to generate key", ret); + goto exit_lms_kg; + } + + /* Store public key to file. */ + ret = lms_export_public_key(key, pubKeyFilename); + +exit_lms_kg: + /* Cleanup. */ + wc_LmsKey_Free(key); + wc_FreeRng(rng); + + return (ret == 0) ? 0 : 1; +} + +#else + +int main() +{ + printf("wolfSSL requires LMS to be compiled in\n"); + return 1; +} + +#endif + diff --git a/pq/stateful_hash_sig/lms-sign.c b/pq/stateful_hash_sig/lms-sign.c new file mode 100644 index 000000000..39119dac1 --- /dev/null +++ b/pq/stateful_hash_sig/lms-sign.c @@ -0,0 +1,378 @@ +/* lms-sign.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Example to demonstrate LMS Key Generation */ +/* + * ./configure --enable-lms && make && sudo make install + * gcc -lwolfssl -o lms-sign lms-sign.c + */ + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#ifdef WOLFSSL_WC_LMS +#include +#else +#include +#endif +#include +#include + +#include + +#ifdef WOLFSSL_HAVE_LMS + +void print_wolfssl_error(const char* msg, int err) +{ +#ifndef NO_ERROR_STRINGS + printf("%s: %s (%d)\n", msg, wc_GetErrorString(err), err); +#else + printf("%s: %d\n", msg, err); +#endif +} + +static int read_file(const char* filename, unsigned char* data, int* sz) +{ + FILE* f = NULL; + int err = 1; + int fileSz; + + printf(" Reading from file: %s\n", filename); + + /* Open file. */ + f = fopen(filename, "rb"); + if (f == NULL) { + printf(" unable to open public key\n"); + goto load_end; + } + + /* Get length of file. */ + fseek(f, 0, SEEK_END); + fileSz = ftell(f); + /* Check buffer is big enough. */ + if (fileSz > *sz) { + printf(" File %s exceeds max size: %d > %d\n", filename, fileSz, + *sz); + goto load_end; + } + /* Go back to start of file. */ + fseek(f, 0, SEEK_SET); + + /* Read in all of file. */ + fileSz = fread(data, 1, fileSz, f); + printf(" Read %d bytes\n", fileSz); + + *sz = fileSz; + err = 0; +load_end: + fclose(f); + return err; +} + +static int write_file(const char* filename, const unsigned char* data, + word32 sz) +{ + FILE* f; + int err = 1; + + printf(" Writing to file: %s\n", filename); + + /* Open file to put LMS private into. */ + f = fopen(filename, "wb"); + if (f == NULL) { + printf(" unable to write to file\n"); + goto write_file_end; + } + + /* Write out LMS private key. */ + fwrite(data, 1, sz, f); + fclose(f); + + err = 0; +write_file_end: + return err; +} + +static int lms_read_key_file(byte* priv, word32 privSz, void *context) +{ + int ret = WC_LMS_RC_READ_FAIL; + const char* filename = (const char*)context; + int sz = (int)privSz; + + /* Read private key file. */ + if (read_file(filename, priv, &sz) != 0) { + } + /* Check number of bytes read matches expected. */ + else if (sz != privSz) { + printf("Size doesn't match expected: %d != %d\n", sz, privSz); + } + else { + /* Success return. */ + ret = WC_LMS_RC_READ_TO_MEMORY; + } + + return ret; +} + +static int lms_write_key_file(const byte* priv, word32 privSz, void *context) +{ + int ret = WC_LMS_RC_WRITE_FAIL; + const char* filename = (const char*)context; + + /* Write the updated private key file. */ + if (write_file(filename, priv, privSz) == 0) + ret = WC_LMS_RC_SAVED_TO_NV_MEMORY; + + return ret; +} + + +static int lms_sign_files(LmsKey* key, int argc, char* argv[]) +{ + int ret; + unsigned char* sig = NULL; + word32 sigSz; + word32 sigLen; + unsigned char msg[1024*1024]; + int msgSz; + char sigFilename[128]; + + /* Get signature length. */ + ret = wc_LmsKey_GetSigLen(key, &sigLen); + if (ret != 0) { + print_wolfssl_error("Failed to get signature length", ret); + goto exit_lms_sign_files; + } + + /* Allocate buffer for signature. */ + sig = malloc(sigLen); + if (sig == NULL) { + ret = MEMORY_E; + printf("Failed to allocate me memory\n"); + goto exit_lms_sign_files; + } + + /* Process all filenames. */ + while (argc > 0) { + sigSz = sigLen; + msgSz = (int)sizeof(msg); + + /* Read file to sign. */ + ret = read_file(argv[0], msg, &msgSz); + if (ret != 0) { + goto exit_lms_sign_files; + } + + /* Sign data in file. */ + ret = wc_LmsKey_Sign(key, sig, &sigSz, msg, msgSz); + if (ret != 0) { + print_wolfssl_error("Failed to sign message", ret); + goto exit_lms_sign_files; + } + + /* Create signature filename. */ + strncpy(sigFilename, argv[0], sizeof(sigFilename) - 5); + sigFilename[sizeof(sigFilename) - 5] = '\0'; + strcat(sigFilename, ".sig"); + /* Write signature to file. */ + ret = write_file(sigFilename, sig, sigSz); + if (ret != 0) { + goto exit_lms_sign_files; + } + + argc--; + argv++; + } + +exit_lms_sign_files: + if (sig != NULL) + free(sig); + return ret; +} + +int main(int argc, char* argv[]) +{ + int ret; + LmsKey key[1]; + const char* filename = "lms_key.bin"; + int levels = 1; + int height = 10; + int winternitz = 4; + int params = -1; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + XMEMSET(key, 0, sizeof(key)); + + argc--; + argv++; + while (argc > 0) { + /* Number of levels of trees. */ + if (strcmp(argv[0], "--levels") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No level value supplied\n"); + goto exit_lms_sign; + } + levels = atoi(argv[0]); + if ((levels < 1) || (levels > 4)) { + ret = -1; + printf("Invalid levels (1-4): %d\n", levels); + } + } + /* Height of each tree. */ + else if (strcmp(argv[0], "--height") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No height value supplied\n"); + goto exit_lms_sign; + } + height = atoi(argv[0]); + if ((height != 5) && (height != 10) && (height != 15) && + (height != 20)) { + ret = -1; + printf("Invalid height (5, 10, 15, 20): %d\n", height); + } + } + /* Winternitz value. */ + else if (strcmp(argv[0], "--winternitz") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No winternitz value supplied\n"); + goto exit_lms_sign; + } + winternitz = atoi(argv[0]); + if ((winternitz != 1) && (winternitz != 2) && (winternitz != 4) && + (winternitz != 8)) { + ret = -1; + printf("Invalid height (1, 2, 4, 8): %d\n", winternitz); + } + } + /* Parameters id. */ + else if (strcmp(argv[0], "--params") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No params value supplied\n"); + goto exit_lms_sign; + } + params = atoi(argv[0]); + if ((params < 1) || (params > 60)) { + ret = -1; + printf("Invalid params (1-60): %d\n", params); + goto exit_lms_sign; + } + } + else if (argv[0][0] == '-') { + printf("Unrecognized option: %s\n", argv[0]); + goto exit_lms_sign; + } + else { + /* Expecting filenames. */ + break; + } + + argc--; + argv++; + } + + /* Initialize the LMS key. */ + ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID); + if (ret != 0) { + print_wolfssl_error("Failed to initialize LMS key", ret); + goto exit_lms_sign; + } + + /* Set parameters to use. */ + if (params == -1) { + printf("Levels: %d, Height: %d, Winternitz: %d\n", levels, height, + winternitz); + ret = wc_LmsKey_SetParameters(key, levels, height, winternitz); + } + else { + printf("Using parameters: %s\n", wc_LmsKey_ParmToStr(params)); + ret = wc_LmsKey_SetLmsParm(key, params); + } + if (ret != 0) { + print_wolfssl_error("Parameters for LMS not valid", ret); + goto exit_lms_sign; + } + + /* Set read callback to get private key. */ + ret = wc_LmsKey_SetReadCb(key, lms_read_key_file); + if (ret != 0) { + print_wolfssl_error("Could not set write callback", ret); + goto exit_lms_sign; + } + /* Set write callback to write out updated private key. */ + ret = wc_LmsKey_SetWriteCb(key, lms_write_key_file); + if (ret != 0) { + print_wolfssl_error("Could not set write callback", ret); + goto exit_lms_sign; + } + + /* Set callbacks' context - the filename to write private key to. */ + ret = wc_LmsKey_SetContext(key, (void*)filename); + if (ret != 0) { + print_wolfssl_error("Could not set context for read/write callbacks", + ret); + goto exit_lms_sign; + } + + /* Reload the private key. */ + ret = wc_LmsKey_Reload(key); + if (ret != 0) { + print_wolfssl_error("Failed to reload key", ret); + goto exit_lms_sign; + } + + printf("Ready to sign\n"); + + /* Sign files. */ + ret = lms_sign_files(key, argc, argv); + +exit_lms_sign: + wc_LmsKey_Free(key); + + return (ret == 0) ? 0 : 1; +} + +#else + +int main() +{ + printf("wolfSSL requires LMS to be compiled in\n"); + return 1; +} + +#endif + diff --git a/pq/stateful_hash_sig/lms-verify.c b/pq/stateful_hash_sig/lms-verify.c new file mode 100644 index 000000000..58b538f14 --- /dev/null +++ b/pq/stateful_hash_sig/lms-verify.c @@ -0,0 +1,340 @@ +/* lms-verify.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Example to demonstrate LMS Key Generation */ +/* + * ./configure --enable-lms && make && sudo make install + * gcc -lwolfssl -o lms-verify lms-verify.c + */ + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#ifdef WOLFSSL_WC_LMS +#include +#else +#include +#endif +#include +#include + +#include + +#ifdef WOLFSSL_HAVE_LMS + +void print_wolfssl_error(const char* msg, int err) +{ +#ifndef NO_ERROR_STRINGS + printf("%s: %s (%d)\n", msg, wc_GetErrorString(err), err); +#else + printf("%s: %d\n", msg, err); +#endif +} + +static int read_file(const char* filename, unsigned char* data, int* sz) +{ + FILE* f = NULL; + int err = 1; + int fileSz; + + printf(" Reading from file: %s\n", filename); + + /* Open file. */ + f = fopen(filename, "rb"); + if (f == NULL) { + printf(" unable to open public key\n"); + goto load_end; + } + + /* Get length of file. */ + fseek(f, 0, SEEK_END); + fileSz = ftell(f); + /* Check buffer is big enough. */ + if (fileSz > *sz) { + printf(" File %s exceeds max size: %d > %d\n", filename, fileSz, + *sz); + goto load_end; + } + /* Go back to start of file. */ + fseek(f, 0, SEEK_SET); + + /* Read in all of file. */ + fileSz = fread(data, 1, fileSz, f); + printf(" Read %d bytes\n", fileSz); + + *sz = fileSz; + err = 0; +load_end: + fclose(f); + return err; +} + +int lms_load_public_key(LmsKey* key, const char* filename) +{ + int ret; + unsigned char* pub = NULL; + word32 pubSz; + int sz; + + /* Get public key length. */ + ret = wc_LmsKey_GetPubLen(key, &pubSz); + if (ret != 0) { + print_wolfssl_error("Failed to import publc key", ret); + goto exit_lms_load_public_key; + } + + /* Allocate memory for public key to be loaded into. */ + pub = malloc(pubSz); + if (pub == NULL) { + ret = MEMORY_E; + printf("Failed to allocate memory: %d bytes\n", pubSz); + goto exit_lms_load_public_key; + } + + sz = (int)pubSz; + /* Read the public key from file. */ + if (read_file(filename, pub, &sz) != 0) { + ret = IO_FAILED_E; + goto exit_lms_load_public_key; + } + + /* Import public key into LMS key. */ + ret = wc_LmsKey_ImportPubRaw(key, pub, pubSz); + if (ret != 0) { + print_wolfssl_error("Failed to import publc key", ret); + goto exit_lms_load_public_key; + } + +exit_lms_load_public_key: + if (pub != NULL) + free(pub); + return ret; +} + +int lms_verify_file(LmsKey* key, const char* filename) +{ + int ret; + unsigned char* sig = NULL; + word32 sigSz; + int sigLen; + unsigned char msg[1024*1024]; + int msgSz; + char sigFilename[128]; + + /* Get the length of a signature. */ + ret = wc_LmsKey_GetSigLen(key, &sigSz); + if (ret != 0) { + print_wolfssl_error("Failed to get signature length", ret); + goto exit_lms_verify_files; + } + + /* Allocate memory for signature to be put into. */ + sig = malloc(sigSz); + if (sig == NULL) { + ret = MEMORY_E; + printf("Failed to allocate me memory\n"); + goto exit_lms_verify_files; + } + + sigLen = sigSz; + msgSz = (int)sizeof(msg); + + /* Read file to verify. */ + ret = read_file(filename, msg, &msgSz); + if (ret != 0) { + goto exit_lms_verify_files; + } + + /* Create signature filename. */ + strncpy(sigFilename, filename, sizeof(sigFilename) - 5); + sigFilename[sizeof(sigFilename) - 5] = '\0'; + strcat(sigFilename, ".sig"); + /* Read in signature from file. */ + ret = read_file(sigFilename, sig, &sigLen); + if (ret != 0) { + goto exit_lms_verify_files; + } + /* Check signature is the expected size. */ + if (sigLen != (int)sigSz) { + ret = IO_FAILED_E; + printf("Signature size does not match expected: %d != %d\n", sigLen, + sigSz); + goto exit_lms_verify_files; + } + + /* Verify the signature against the message. */ + ret = wc_LmsKey_Verify(key, sig, sigSz, msg, msgSz); + if (ret != 0) { + print_wolfssl_error("Failed to verify message", ret); + goto exit_lms_verify_files; + } + printf("Verification succeeded\n"); + +exit_lms_verify_files: + if (sig != NULL) + free(sig); + return ret; +} + +int main(int argc, char* argv[]) +{ + int ret; + LmsKey key[1]; + const char* filename = "lms_pubkey.bin"; + int levels = 1; + int height = 10; + int winternitz = 4; + int params = -1; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif + + XMEMSET(key, 0, sizeof(key)); + + argc--; + argv++; + while (argc > 0) { + /* Number of levels of trees. */ + if (strcmp(argv[0], "--levels") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No level value supplied\n"); + goto exit_lms_verify; + } + levels = atoi(argv[0]); + if ((levels < 1) || (levels > 4)) { + ret = -1; + printf("Invalid levels (1-4): %d\n", levels); + } + } + /* Height of each tree. */ + else if (strcmp(argv[0], "--height") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No height value supplied\n"); + goto exit_lms_verify; + } + height = atoi(argv[0]); + if ((height != 5) && (height != 10) && (height != 15) && + (height != 20)) { + ret = -1; + printf("Invalid height (5, 10, 15, 20): %d\n", height); + } + } + /* Winternitz value. */ + else if (strcmp(argv[0], "--winternitz") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No winternitz value supplied\n"); + goto exit_lms_verify; + } + winternitz = atoi(argv[0]); + if ((winternitz != 1) && (winternitz != 2) && (winternitz != 4) && + (winternitz != 8)) { + ret = -1; + printf("Invalid height (1, 2, 4, 8): %d\n", winternitz); + } + } + /* Parameters id. */ + else if (strcmp(argv[0], "--params") == 0) { + argc--; + argv++; + if (argc == 0) { + ret = -1; + printf("No params value supplied\n"); + goto exit_lms_verify; + } + params = atoi(argv[0]); + if ((params < 1) || (params > 60)) { + ret = -1; + printf("Invalid params (1-60): %d\n", params); + goto exit_lms_verify; + } + } + else if (argv[0][0] == '-') { + printf("Unrecognized option: %s\n", argv[0]); + goto exit_lms_verify; + } + else { + /* Expecting filename. */ + break; + } + + argc--; + argv++; + } + + /* Initialize the LMS key. */ + ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID); + if (ret != 0) { + print_wolfssl_error("Failed to initialize LMS key", ret); + goto exit_lms_verify; + } + + /* Set parameters to use. */ + if (params == -1) { + printf("Levels: %d, Height: %d, Winternitz: %d\n", levels, height, + winternitz); + ret = wc_LmsKey_SetParameters(key, levels, height, winternitz); + } + else { + printf("Using parameters: %s\n", wc_LmsKey_ParmToStr(params)); + ret = wc_LmsKey_SetLmsParm(key, params); + } + if (ret != 0) { + print_wolfssl_error("Parameters for LMS not valid", ret); + goto exit_lms_verify; + } + + /* Load the public key from file. */ + ret = lms_load_public_key(key, filename); + if (ret != 0) { + goto exit_lms_verify; + } + + /* Verify the file with the LMS key. */ + ret = lms_verify_file(key, argv[0]); + +exit_lms_verify: + wc_LmsKey_Free(key); + + return (ret == 0) ? 0 : 1; +} + +#else + +int main() +{ + printf("wolfSSL requires LMS to be compiled in\n"); + return 1; +} + +#endif +