diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e8c1d4e..ba65f004 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ option(ENABLE_SM2_EXTS "Enable SM2 Extensions" OFF) option(ENABLE_LMS_HSS "Enable LMS/HSS signature" ON) option(ENABLE_XMSS "Enable XMSS/XMSS^MT signature" ON) -option(ENABLE_SPHINCS "Enable SPHINCS+ signature" OFF) +option(ENABLE_SPHINCS "Enable SPHINCS+ signature" ON) option(ENABLE_KYBER "Enable Kyber" OFF) option(ENABLE_SHA1 "Enable SHA1" ON) @@ -454,7 +454,7 @@ if (ENABLE_SPHINCS) message(STATUS "ENABLE_SPHINCS is ON") add_definitions(-DENABLE_SPHINCS) list(APPEND src src/sphincs.c) - #list(APPEND tools tools/sphincskeygen.c tools/sphincssign.c tools/sphincsverify.c) + list(APPEND tools tools/sphincskeygen.c tools/sphincssign.c tools/sphincsverify.c) list(APPEND tests sphincs) option(ENABLE_SPHINCS_CROSSCHECK "Enable SPHINCS SHA-256 cross-check" ON) @@ -486,6 +486,7 @@ if (ENABLE_SHA2) message(STATUS "ENABLE_SHA2 is ON") add_definitions(-DENABLE_SHA2) list(APPEND src src/sha256.c src/sha512.c) + list(APPEND src src/sha256_hmac.c) list(APPEND tests sha224 sha256 sha384 sha512) endif() diff --git a/include/gmssl/sphincs.h b/include/gmssl/sphincs.h index 8a0ddc16..b221c36e 100644 --- a/include/gmssl/sphincs.h +++ b/include/gmssl/sphincs.h @@ -32,12 +32,20 @@ extern "C" { # define hash256_update sha256_update # define hash256_finish sha256_finish # define HASH256_BLOCK_SIZE SHA256_BLOCK_SIZE +# define HASH256_HMAC_CTX SHA256_HMAC_CTX +# define hash256_hmac_init sha256_hmac_init +# define hash256_hmac_update sha256_hmac_update +# define hash256_hmac_finish sha256_hmac_finish #else # define HASH256_CTX SM3_CTX # define hash256_init sm3_init # define hash256_update sm3_update # define hash256_finish sm3_finish # define HASH256_BLOCK_SIZE SM3_BLOCK_SIZE +# define HASH256_HMAC_CTX SM3_HMAC_CTX +# define hash256_hmac_init sm3_hmac_init +# define hash256_hmac_update sm3_hmac_update +# define hash256_hmac_finish sm3_hmac_finish #endif @@ -256,7 +264,6 @@ void sphincs_wots_pk_to_root(const sphincs_wots_key_t pk, total: 30 bytes, 1 MGF1-SM3 output - SPHINCS+_128f/SM3 1. fors_index: 6 * 33 = 198 bits = 25 bytes @@ -266,6 +273,14 @@ void sphincs_wots_pk_to_root(const sphincs_wots_key_t pk, */ +#define SPHINCS_TBS_SIZE 30 // sphincs+_128s + +#define SPHINCS_TBS_FORS_SIZE 21 +#define SPHINCS_TBS_TREE_ADDRESS_SIZE 7 +#define SPHINCS_TBS_KEYPAIR_ADDRESS_SIZE 2 + + +// #define SPHINCS_TBS_SIZE 34 // sphincs+_128f void sphincs_xmss_tree_hash( @@ -285,9 +300,15 @@ void sphincs_xmss_build_root(const sphincs_hash128_t wots_root, uint32_t tree_in typedef struct { sphincs_wots_sig_t wots_sig; - sphincs_hash128_t auth_path[22]; // sphincs+_128f height = 22 + sphincs_hash128_t auth_path[SPHINCS_XMSS_HEIGHT]; } SPHINCS_XMSS_SIGNATURE; +int sphincs_xmss_signature_print_ex(FILE *fp, int fmt, int ind, const char *label, const SPHINCS_XMSS_SIGNATURE *sig); +int sphincs_xmss_signature_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *sig, size_t siglen); +int sphincs_xmss_signature_to_bytes(const SPHINCS_XMSS_SIGNATURE *sig, uint8_t **out, size_t *outlen); +int sphincs_xmss_signature_from_bytes(SPHINCS_XMSS_SIGNATURE *sig, const uint8_t **in, size_t *inlen); + + void sphincs_xmss_sign(const sphincs_hash128_t secret, const sphincs_hash128_t seed, const sphincs_adrs_t adrs, uint32_t keypair_address, const sphincs_hash128_t tbs_root, // to be signed xmss_root or fors_forest_root @@ -361,6 +382,8 @@ typedef struct { #define SPHINCS_PUBLIC_KEY_SIZE sizeof(SPHINCS_PUBLIC_KEY) #define SPHINCS_PRIVATE_KEY_SIZE sizeof(SPHINCS_KEY) +int sphincs_key_generate(SPHINCS_KEY *key); + int sphincs_public_key_to_bytes(const SPHINCS_KEY *key, uint8_t **out, size_t *outlen); int sphincs_public_key_from_bytes(SPHINCS_KEY *key, const uint8_t **in, size_t *inlen); int sphincs_public_key_print(FILE *fp, int fmt, int ind, const char *label, const SPHINCS_KEY *key); @@ -370,12 +393,15 @@ int sphincs_private_key_print(FILE *fp, int fmt, int ind, const char *label, con void sphincs_key_cleanup(SPHINCS_KEY *key); + + typedef struct { sphincs_hash128_t random; SPHINCS_FORS_SIGNATURE fors_sig; SPHINCS_XMSS_SIGNATURE xmss_sigs[SPHINCS_HYPERTREE_LAYERS]; } SPHINCS_SIGNATURE; +#define SPHINCS_SIGNATURE_SIZE sizeof(SPHINCS_SIGNATURE) int sphincs_signature_to_bytes(const SPHINCS_SIGNATURE *sig, uint8_t **out, size_t *outlen); int sphincs_signature_from_bytes(SPHINCS_SIGNATURE *sig, const uint8_t **in, size_t *inlen); int sphincs_signature_print_ex(FILE *fp, int fmt, int ind, const char *label, const SPHINCS_SIGNATURE *sig); @@ -383,6 +409,41 @@ int sphincs_signature_print(FILE *fp, int fmt, int ind, const char *label, const + +typedef struct { + HASH256_HMAC_CTX hmac_ctx; + HASH256_CTX hash_ctx; + SPHINCS_SIGNATURE sig; + int state; // after init 0, after prepare 1, after update 2 + size_t round1_msglen; + size_t round2_msglen; + SPHINCS_KEY key; +} SPHINCS_SIGN_CTX; + +int sphincs_sign_init_ex(SPHINCS_SIGN_CTX *ctx, const SPHINCS_KEY *key, int randomize); +int sphincs_sign_init(SPHINCS_SIGN_CTX *ctx, const SPHINCS_KEY *key); +int sphincs_sign_prepare(SPHINCS_SIGN_CTX *ctx, const uint8_t *data, size_t datalen); +int sphincs_sign_update(SPHINCS_SIGN_CTX *ctx, const uint8_t *data, size_t datalen); +int sphincs_sign_finish_ex(SPHINCS_SIGN_CTX *ctx, SPHINCS_SIGNATURE *sig); +int sphincs_sign_finish(SPHINCS_SIGN_CTX *ctx, uint8_t *sig, size_t *siglen); +int sphincs_verify_init_ex(SPHINCS_SIGN_CTX *ctx, const SPHINCS_KEY *key, const SPHINCS_SIGNATURE *sig); +int sphincs_verify_init(SPHINCS_SIGN_CTX *ctx, const SPHINCS_KEY *key, const uint8_t *sig, size_t siglen); +int sphincs_verify_update(SPHINCS_SIGN_CTX *ctx, const uint8_t *data, size_t datalen); +int sphincs_verify_finish(SPHINCS_SIGN_CTX *ctx); + + + + + + + + + + + + + + #ifdef __cplusplus } #endif diff --git a/tests/sphincstest.c b/tests/sphincstest.c index 0d8f6d98..3366ff11 100644 --- a/tests/sphincstest.c +++ b/tests/sphincstest.c @@ -425,29 +425,212 @@ static int test_sphincs_fors_sign(void) return 1; } + static int test_sphincs_sign(void) { - SPHINCS_KEY key; + SPHINCS_KEY _key; + SPHINCS_KEY *key = &_key; + uint8_t msg[100] = {1, 2, 3, 0}; + SPHINCS_SIGNATURE _sig; + SPHINCS_SIGNATURE *sig = &_sig; + HASH256_CTX hash_ctx; + HASH256_HMAC_CTX hmac_ctx; + hash256_t dgst; + + + sphincs_hash128_t opt_rand; + + sphincs_adrs_t adrs = {0}; - sphincs_hash128_t random; + sphincs_hash128_t fors_forest_root; + + uint8_t tree_address_buf[8] = {0}; + uint8_t keypair_address_buf[4] = {0}; + + uint64_t tree_address; + uint32_t keypair_address; + + int randomize = 0; + uint32_t i; + + uint8_t tbs[SPHINCS_TBS_SIZE]; + + if (sphincs_key_generate(key) != 1) { + error_print(); + return -1; + } - uint32_t tree_index; - uint32_t leaf_index; + // set opt_rand + memcpy(opt_rand, key->public_key.seed, sizeof(sphincs_hash128_t)); + if (randomize) { + if (rand_bytes(opt_rand, sizeof(opt_rand)) != 1) { + error_print(); + return -1; + } + } + // 如果R是用M生成的,这意味着M要读取2遍,这就没办法用init/update范式了 + + // R = PRF_msg(sk_prf, optrand, M) = HMAC(sk_prf, opt_rand|M) + hash256_hmac_init(&hmac_ctx, key->sk_prf, sizeof(sphincs_hash128_t)); + hash256_hmac_update(&hmac_ctx, opt_rand, sizeof(sphincs_hash128_t)); + hash256_hmac_update(&hmac_ctx, msg, sizeof(msg)); + hash256_hmac_finish(&hmac_ctx, dgst); + memcpy(sig->random, dgst, sizeof(sphincs_hash128_t)); + + // dgst = HASH256(R|seed|root|M) + hash256_init(&hash_ctx); + hash256_update(&hash_ctx, sig->random, sizeof(sphincs_hash128_t)); + hash256_update(&hash_ctx, key->public_key.seed, sizeof(sphincs_hash128_t)); + hash256_update(&hash_ctx, key->public_key.root, sizeof(sphincs_hash128_t)); + hash256_update(&hash_ctx, msg, sizeof(msg)); + hash256_finish(&hash_ctx, dgst); + + // tbs = H_msg(R, seed, root, M) = MGF1(R|seed|dgst, tbs_len) + for (i = 0; i < (SPHINCS_TBS_SIZE + 31)/32; i++) { + uint8_t count[4]; + PUTU32(count, i); + hash256_init(&hash_ctx); + hash256_update(&hash_ctx, sig->random, sizeof(sphincs_hash128_t)); + hash256_update(&hash_ctx, key->public_key.seed, sizeof(sphincs_hash128_t)); + hash256_update(&hash_ctx, dgst, sizeof(dgst)); + hash256_update(&hash_ctx, count, sizeof(count)); + hash256_finish(&hash_ctx, tbs + sizeof(dgst) * i); + } + + + // get tree_address from tbs + memcpy(tree_address_buf + 8 - 7, tbs + 21, 7); + tree_address = GETU64(tree_address_buf); // 54 bits + tree_address >>= 10; + + // get keypair_address from tbs + memcpy(keypair_address_buf + 4 - 2, tbs + 21 + 7, 2); + keypair_address = GETU32(keypair_address_buf); + keypair_address >>= (16 - 9); + + + + format_bytes(stderr, 0, 4, "tree_address", tree_address_buf, 8); + format_bytes(stderr, 0, 4, "keypair_address", keypair_address_buf, 4); + + + fprintf(stderr, "tree_address %zu\n", (size_t)tree_address); + fprintf(stderr, "keypair_address %zu\n", (size_t)keypair_address); + + + + + + + // fors_sign sphincs_adrs_set_layer_address(adrs, 0); - sphincs_adrs_set_tree_address(adrs, tree_index); + sphincs_adrs_set_tree_address(adrs, tree_address); sphincs_adrs_set_type(adrs, SPHINCS_ADRS_TYPE_FORS_TREE); - sphincs_adrs_set_keypair_address(adrs, leaf_index); + sphincs_adrs_set_keypair_address(adrs, keypair_address); + sphincs_fors_sign(key->secret, key->public_key.seed, adrs, tbs, &sig->fors_sig); + + // fors_sig => fors_forest_root + sphincs_fors_sig_to_root(&sig->fors_sig, key->public_key.seed, adrs, tbs, fors_forest_root); + + + error_print(); + + + + // hypertree_sign fors_forest_root + sphincs_adrs_set_type(adrs, SPHINCS_ADRS_TYPE_TREE); + sphincs_hypertree_sign(key->secret, key->public_key.seed, tree_address, keypair_address, + fors_forest_root, sig->xmss_sigs); + + + error_print(); + + + + // sphincs_verify + // -------------- + + + // fors_sig => fors_forest_root + sphincs_adrs_set_layer_address(adrs, 0); + sphincs_adrs_set_tree_address(adrs, tree_address); + sphincs_adrs_set_type(adrs, SPHINCS_ADRS_TYPE_FORS_TREE); + sphincs_adrs_set_keypair_address(adrs, keypair_address); + + sphincs_fors_sig_to_root(&sig->fors_sig, key->public_key.seed, adrs, tbs, fors_forest_root); + + // hypertree_verify + sphincs_adrs_set_type(adrs, SPHINCS_ADRS_TYPE_TREE); + + if (sphincs_hypertree_verify(key->public_key.root, key->public_key.seed, + tree_address, keypair_address, fors_forest_root, sig->xmss_sigs) != 1) { + error_print(); + return -1; + } printf("%s() ok\n", __FUNCTION__); return 1; } + +static int test_sphincs_sign_update(void) +{ + SPHINCS_KEY key; + SPHINCS_SIGN_CTX ctx; + SPHINCS_SIGNATURE sig; + uint8_t msg[100] = { 1,2,3 }; + + if (sphincs_key_generate(&key) != 1) { + error_print(); + return -1; + } + + if (sphincs_sign_init(&ctx, &key) != 1) { + error_print(); + return -1; + } + if (sphincs_sign_prepare(&ctx, msg, sizeof(msg)) != 1) { + error_print(); + return -1; + } + if (sphincs_sign_update(&ctx, msg, sizeof(msg)) != 1) { + error_print(); + return -1; + } + if (sphincs_sign_finish_ex(&ctx, &sig) != 1) { + error_print(); + return -1; + } + + // verify + + if (sphincs_verify_init_ex(&ctx, &key, &sig) != 1) { + error_print(); + return -1; + } + if (sphincs_verify_update(&ctx, msg, sizeof(msg)) != 1) { + error_print(); + return -1; + } + if (sphincs_verify_finish(&ctx) != 1) { + error_print(); + return -1; + } + + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + + int main(void) { - if (test_sphincs_fors_sign() != 1) goto err; + if (test_sphincs_sign_update() != 1) goto err; +// if (test_sphincs_sign() != 1) goto err; +// if (test_sphincs_fors_sign() != 1) goto err; //if (test_sphincs_xmss_build_tree() != 1) goto err; //if (test_sphincs_hypertree() != 1) goto err; // if (test_sphincs_hypertree_sign() != 1) goto err; diff --git a/tools/gmssl.c b/tools/gmssl.c index eeb65eac..460a0fee 100644 --- a/tools/gmssl.c +++ b/tools/gmssl.c @@ -80,6 +80,11 @@ extern int xmssmtkeygen_main(int argc, char **argv); extern int xmssmtsign_main(int argc, char **argv); extern int xmssmtverify_main(int argc, char **argv); #endif +#ifdef ENABLE_SPHINCS +extern int sphincskeygen_main(int argc, char **argv); +extern int sphincssign_main(int argc, char **argv); +extern int sphincsverify_main(int argc, char **argv); +#endif #ifdef ENABLE_KYBER extern int kyberkeygen_main(int argc, char **argv); extern int kyberencap_main(int argc, char **argv); @@ -165,6 +170,11 @@ static const char *options = " xmssmtsign Generate XMSS^MT-SM3 signature\n" " xmssmtverify Verify XMSS^MT-SM3 signature\n" #endif +#ifdef ENABLE_SPHINCS + " sphincskeygen Generate SPHINCS+_128s-SM3 keypair\n" + " sphincssign Generate SPHINCS+_128s-SM3 signature\n" + " sphincsverify Verify SPHINCS+_128s-SM3 signature\n" +#endif #ifdef ENABLE_KYBER " kyberkeygen Generate Kyber keypair\n" " kyberencap Kyber KEM encap\n" @@ -352,6 +362,14 @@ int main(int argc, char **argv) } else if (!strcmp(*argv, "xmssmtverify")) { return xmssmtverify_main(argc, argv); #endif +#ifdef ENABLE_SPHINCS + } else if (!strcmp(*argv, "sphincskeygen")) { + return sphincskeygen_main(argc, argv); + } else if (!strcmp(*argv, "sphincssign")) { + return sphincssign_main(argc, argv); + } else if (!strcmp(*argv, "sphincsverify")) { + return sphincsverify_main(argc, argv); +#endif #ifdef ENABLE_KYBER } else if (!strcmp(*argv, "kyberkeygen")) { return kyberkeygen_main(argc, argv); diff --git a/tools/sphincskeygen.c b/tools/sphincskeygen.c new file mode 100644 index 00000000..caf9d3cb --- /dev/null +++ b/tools/sphincskeygen.c @@ -0,0 +1,132 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include + + +static const char *usage = "-out file [-pubout file] [-verbose]\n"; + +static const char *options = +"Options\n" +" -out file Output private key\n" +" -pubout file Output public key\n" +" -verbose Print public key\n" +"\n"; + +int sphincskeygen_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *outfile = NULL; + char *puboutfile = NULL; + int verbose = 0; + FILE *outfp = NULL; + FILE *puboutfp = stdout; + SPHINCS_KEY key; + uint8_t out[SPHINCS_PRIVATE_KEY_SIZE]; + uint8_t *pout = out; + size_t outlen = 0; + uint8_t pubout[SPHINCS_PUBLIC_KEY_SIZE]; + uint8_t *ppubout = pubout; + size_t puboutlen = 0; + + memset(&key, 0, sizeof(key)); + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: gmssl %s %s\n", prog, usage); + printf("%s\n", options); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "%s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-pubout")) { + if (--argc < 1) goto bad; + puboutfile = *(++argv); + if (!(puboutfp = fopen(puboutfile, "wb"))) { + fprintf(stderr, "%s: open '%s' failure : %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "%s: `%s` option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!outfp) { + fprintf(stderr, "%s: `-out` option required\n", prog); + goto end; + } + + if (sphincs_key_generate(&key) != 1) { + error_print(); + return -1; + } + if (verbose) { + sphincs_public_key_print(stderr, 0, 0, "sphincs_public_key", &key); + } + + if (sphincs_private_key_to_bytes(&key, &pout, &outlen) != 1) { + error_print(); + goto end; + } + if (fwrite(out, 1, outlen, outfp) != outlen) { + error_print(); + goto end; + } + + if (sphincs_public_key_to_bytes(&key, &ppubout, &puboutlen) != 1) { + error_print(); + goto end; + } + if (puboutlen != sizeof(pubout)) { + error_print(); + goto end; + } + if (fwrite(pubout, 1, puboutlen, puboutfp) != puboutlen) { + error_print(); + goto end; + } + + ret = 0; +end: + sphincs_key_cleanup(&key); + gmssl_secure_clear(out, sizeof(out)); + if (outfile && outfp) fclose(outfp); + if (puboutfile && puboutfp) fclose(puboutfp); + return ret; +} diff --git a/tools/sphincssign.c b/tools/sphincssign.c new file mode 100644 index 00000000..89115936 --- /dev/null +++ b/tools/sphincssign.c @@ -0,0 +1,175 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *usage = "-key file [-in file] [-out file] [-verbose]\n"; + +static const char *options = +"Options\n" +" -key file Input private key file\n" +" -in file Input data file (if not using stdin)\n" +" -out file Output signature file\n" +" -verbose Print public key and signature\n" +"\n"; + +int sphincssign_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *keyfile = NULL; + char *infile = NULL; + char *outfile = NULL; + int verbose = 0; + FILE *keyfp = NULL; + FILE *infp = stdin; + FILE *outfp = stdout; + uint8_t keybuf[SPHINCS_PRIVATE_KEY_SIZE]; + const uint8_t *cp = keybuf; + size_t keylen = sizeof(keybuf); + SPHINCS_KEY key; + SPHINCS_SIGN_CTX ctx; + uint8_t sig[SPHINCS_SIGNATURE_SIZE]; + size_t siglen; + + memset(&key, 0, sizeof(key)); + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: %s %s\n", prog, usage); + printf("%s\n", options); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-key")) { + if (--argc < 1) goto bad; + keyfile = *(++argv); + if (!(keyfp = fopen(keyfile, "rb+"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, keyfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-in")) { + if (--argc < 1) goto bad; + infile = *(++argv); + if (!(infp = fopen(infile, "rb"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, infile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-out")) { + if (--argc < 1) goto bad; + outfile = *(++argv); + if (!(outfp = fopen(outfile, "wb"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, outfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "%s: `%s` option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!keyfile) { + fprintf(stderr, "%s: `-key` option required\n", prog); + goto end; + } + if (fread(keybuf, 1, keylen, keyfp) != keylen) { + fprintf(stderr, "%s: read private key failure\n", prog); + goto end; + } + + if (sphincs_private_key_from_bytes(&key, &cp, &keylen) != 1) { + error_print(); + goto end; + } + if (keylen) { + error_print(); + return -1; + } + + if (verbose) { + sphincs_public_key_print(stderr, 0, 0, "sphincs_public_key", &key); + } + + if (sphincs_sign_init(&ctx, &key) != 1) { + error_print(); + goto end; + } + + while (1) { + uint8_t buf[1024]; + size_t len = fread(buf, 1, sizeof(buf), infp); + if (len == 0) { + break; + } + if (sphincs_sign_prepare(&ctx, buf, len) != 1) { + error_print(); + goto end; + } + } + + rewind(infp); + while (1) { + uint8_t buf[1024]; + size_t len = fread(buf, 1, sizeof(buf), infp); + if (len == 0) { + break; + } + if (sphincs_sign_update(&ctx, buf, len) != 1) { + error_print(); + goto end; + } + } + + if (sphincs_sign_finish(&ctx, sig, &siglen) != 1) { + error_print(); + goto end; + } + if (fwrite(sig, 1, siglen, outfp) != siglen) { + error_print(); + goto end; + } + if (verbose) { + sphincs_signature_print(stderr, 0, 0, "sphincs_signature", sig, siglen); + } + + ret = 0; + +end: + sphincs_key_cleanup(&key); + gmssl_secure_clear(keybuf, keylen); + gmssl_secure_clear(&ctx, sizeof(ctx)); + if (keyfp) fclose(keyfp); + if (infp && infp != stdin) fclose(infp); + if (outfp && outfp != stdout) fclose(outfp); + return ret; +} diff --git a/tools/sphincsverify.c b/tools/sphincsverify.c new file mode 100644 index 00000000..1ed3e016 --- /dev/null +++ b/tools/sphincsverify.c @@ -0,0 +1,158 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include + +static const char *usage = "-pubkey file [-in file] -sig file [-verbose]\n"; + +static const char *options = +"Options\n" +" -pubkey file Input public key file\n" +" -in file Input data file (if not using stdin)\n" +" -sig file Input signature file\n" +" -verbose Print public key and signature\n" +"\n"; + +int sphincsverify_main(int argc, char **argv) +{ + int ret = 1; + char *prog = argv[0]; + char *pubkeyfile = NULL; + char *infile = NULL; + char *sigfile = NULL; + int verbose = 0; + FILE *pubkeyfp = NULL; + FILE *infp = stdin; + FILE *sigfp = NULL; + uint8_t pubkeybuf[SPHINCS_PUBLIC_KEY_SIZE]; + size_t pubkeylen = SPHINCS_PUBLIC_KEY_SIZE; + const uint8_t *cp = pubkeybuf; + uint8_t sig[SPHINCS_SIGNATURE_SIZE]; + size_t siglen; + SPHINCS_KEY key; + SPHINCS_SIGN_CTX ctx; + int vr; + + argc--; + argv++; + + if (argc < 1) { + fprintf(stderr, "usage: gmssl %s %s\n", prog, usage); + return 1; + } + + while (argc > 0) { + if (!strcmp(*argv, "-help")) { + printf("usage: %s %s\n", prog, usage); + printf("%s\n", options); + ret = 0; + goto end; + } else if (!strcmp(*argv, "-pubkey")) { + if (--argc < 1) goto bad; + pubkeyfile = *(++argv); + if (!(pubkeyfp = fopen(pubkeyfile, "rb"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, pubkeyfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-in")) { + if (--argc < 1) goto bad; + infile = *(++argv); + if (!(infp = fopen(infile, "rb"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, infile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-sig")) { + if (--argc < 1) goto bad; + sigfile = *(++argv); + if (!(sigfp = fopen(sigfile, "rb"))) { + fprintf(stderr, "%s: open '%s' failure: %s\n", prog, sigfile, strerror(errno)); + goto end; + } + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else { + fprintf(stderr, "%s: illegal option '%s'\n", prog, *argv); + goto end; +bad: + fprintf(stderr, "%s: `%s` option value missing\n", prog, *argv); + goto end; + } + + argc--; + argv++; + } + + if (!pubkeyfile) { + fprintf(stderr, "%s: `-key` option required\n", prog); + goto end; + } + if (!sigfile) { + fprintf(stderr, "%s: `-sig` option required\n", prog); + goto end; + } + + if (fread(pubkeybuf, 1, pubkeylen, pubkeyfp) != pubkeylen) { + fprintf(stderr, "%s: read public key failure\n", prog); + goto end; + } + if (sphincs_public_key_from_bytes(&key, &cp, &pubkeylen) != 1) { + error_print(); + fprintf(stderr, "%s: invalid public key data\n", prog); + goto end; + } + if (verbose) { + sphincs_public_key_print(stderr, 0, 0, "sphincs_public_key", &key); + } + + // read signature even if signature not compatible with the public key + if ((siglen = fread(sig, 1, SPHINCS_SIGNATURE_SIZE, sigfp)) != SPHINCS_SIGNATURE_SIZE) { + fprintf(stderr, "%s: read signature failure\n", prog); + goto end; + } + if (verbose) { + sphincs_signature_print(stderr, 0, 0, "sphincs_signature", sig, siglen); + } + if (sphincs_verify_init(&ctx, &key, sig, siglen) != 1) { + error_print(); + goto end; + } + + while (1) { + uint8_t buf[1024]; + size_t len = fread(buf, 1, sizeof(buf), infp); + if (len == 0) { + break; + } + if (sphincs_verify_update(&ctx, buf, len) != 1) { + error_print(); + goto end; + } + } + if ((vr = sphincs_verify_finish(&ctx)) < 0) { + error_print(); + goto end; + } + fprintf(stdout, "verify : %s\n", vr == 1 ? "success" : "failure"); + if (vr == 1) { + ret = 0; + } + +end: + if (pubkeyfp) fclose(pubkeyfp); + if (infp && infp != stdin) fclose(infp); + if (sigfp) fclose(sigfp); + return ret; +}