From 6bf57f89c965f34cd804a8fc6532f0badf76c72e Mon Sep 17 00:00:00 2001 From: Douglas Stebila Date: Wed, 17 Jul 2019 23:15:38 -0400 Subject: [PATCH] Add SPHINCS+ SHA-256 variants --- VisualStudio/oqs/dll.def | 36 ++ VisualStudio/oqs/oqs.vcxproj | 108 ++++++ VisualStudio/oqs/oqs.vcxproj.filters | 360 ++++++++++++++++++ VisualStudio/winconfig.h | 12 + config/features.m4 | 12 + .../copy_from_pqclean/copy_from_pqclean.yml | 84 ++++ src/sig/sig.c | 74 +++- src/sig/sig.h | 26 +- src/sig/sphincs/Makefile.am | 40 +- .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_robust.c | 82 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ .../LICENSE | 116 ++++++ .../address.c | 78 ++++ .../address.h | 50 +++ .../api.h | 78 ++++ .../fors.c | 169 ++++++++ .../fors.h | 32 ++ .../hash.h | 29 ++ .../hash_sha256.c | 157 ++++++++ .../hash_state.h | 7 + .../params.h | 53 +++ .../sha256.c | 71 ++++ .../sha256.h | 21 + .../sign.c | 353 +++++++++++++++++ .../thash.h | 27 ++ .../thash_sha256_simple.c | 75 ++++ .../utils.c | 199 ++++++++++ .../utils.h | 64 ++++ .../wots.c | 167 ++++++++ .../wots.h | 41 ++ src/sig/sphincs/sig_sphincs.h | 132 +++++++ .../sphincs/sig_sphincs_sha256_128f_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_128f_simple.c | 44 +++ .../sphincs/sig_sphincs_sha256_128s_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_128s_simple.c | 44 +++ .../sphincs/sig_sphincs_sha256_192f_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_192f_simple.c | 44 +++ .../sphincs/sig_sphincs_sha256_192s_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_192s_simple.c | 44 +++ .../sphincs/sig_sphincs_sha256_256f_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_256f_simple.c | 44 +++ .../sphincs/sig_sphincs_sha256_256s_robust.c | 44 +++ .../sphincs/sig_sphincs_sha256_256s_simple.c | 44 +++ 250 files changed, 22894 insertions(+), 4 deletions(-) create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash_sha256_robust.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/LICENSE create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/api.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_state.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/params.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sign.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash_sha256_simple.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.h create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.c create mode 100644 src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.h create mode 100644 src/sig/sphincs/sig_sphincs_sha256_128f_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_128f_simple.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_128s_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_128s_simple.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_192f_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_192f_simple.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_192s_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_192s_simple.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_256f_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_256f_simple.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_256s_robust.c create mode 100644 src/sig/sphincs/sig_sphincs_sha256_256s_simple.c diff --git a/VisualStudio/oqs/dll.def b/VisualStudio/oqs/dll.def index 6a94d110c..b5ad11ac8 100644 --- a/VisualStudio/oqs/dll.def +++ b/VisualStudio/oqs/dll.def @@ -159,6 +159,42 @@ EXPORTS OQS_SIG_sphincs_haraka_256s_simple_keypair OQS_SIG_sphincs_haraka_256s_simple_sign OQS_SIG_sphincs_haraka_256s_simple_verify + OQS_SIG_sphincs_sha256_128f_robust_keypair + OQS_SIG_sphincs_sha256_128f_robust_sign + OQS_SIG_sphincs_sha256_128f_robust_verify + OQS_SIG_sphincs_sha256_128f_simple_keypair + OQS_SIG_sphincs_sha256_128f_simple_sign + OQS_SIG_sphincs_sha256_128f_simple_verify + OQS_SIG_sphincs_sha256_128s_robust_keypair + OQS_SIG_sphincs_sha256_128s_robust_sign + OQS_SIG_sphincs_sha256_128s_robust_verify + OQS_SIG_sphincs_sha256_128s_simple_keypair + OQS_SIG_sphincs_sha256_128s_simple_sign + OQS_SIG_sphincs_sha256_128s_simple_verify + OQS_SIG_sphincs_sha256_192f_robust_keypair + OQS_SIG_sphincs_sha256_192f_robust_sign + OQS_SIG_sphincs_sha256_192f_robust_verify + OQS_SIG_sphincs_sha256_192f_simple_keypair + OQS_SIG_sphincs_sha256_192f_simple_sign + OQS_SIG_sphincs_sha256_192f_simple_verify + OQS_SIG_sphincs_sha256_192s_robust_keypair + OQS_SIG_sphincs_sha256_192s_robust_sign + OQS_SIG_sphincs_sha256_192s_robust_verify + OQS_SIG_sphincs_sha256_192s_simple_keypair + OQS_SIG_sphincs_sha256_192s_simple_sign + OQS_SIG_sphincs_sha256_192s_simple_verify + OQS_SIG_sphincs_sha256_256f_robust_keypair + OQS_SIG_sphincs_sha256_256f_robust_sign + OQS_SIG_sphincs_sha256_256f_robust_verify + OQS_SIG_sphincs_sha256_256f_simple_keypair + OQS_SIG_sphincs_sha256_256f_simple_sign + OQS_SIG_sphincs_sha256_256f_simple_verify + OQS_SIG_sphincs_sha256_256s_robust_keypair + OQS_SIG_sphincs_sha256_256s_robust_sign + OQS_SIG_sphincs_sha256_256s_robust_verify + OQS_SIG_sphincs_sha256_256s_simple_keypair + OQS_SIG_sphincs_sha256_256s_simple_sign + OQS_SIG_sphincs_sha256_256s_simple_verify OQS_SIG_sphincs_shake256_128f_robust_keypair OQS_SIG_sphincs_shake256_128f_robust_sign OQS_SIG_sphincs_shake256_128f_robust_verify diff --git a/VisualStudio/oqs/oqs.vcxproj b/VisualStudio/oqs/oqs.vcxproj index b7abd8465..02e289724 100644 --- a/VisualStudio/oqs/oqs.vcxproj +++ b/VisualStudio/oqs/oqs.vcxproj @@ -356,6 +356,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VisualStudio/oqs/oqs.vcxproj.filters b/VisualStudio/oqs/oqs.vcxproj.filters index 5abc55470..682e8a381 100644 --- a/VisualStudio/oqs/oqs.vcxproj.filters +++ b/VisualStudio/oqs/oqs.vcxproj.filters @@ -767,6 +767,330 @@ sphincs\haraka_256s_simple + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_robust + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128f_simple + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_robust + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_128s_simple + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_robust + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192f_simple + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_robust + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_192s_simple + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_robust + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256f_simple + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_robust + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + + + sphincs\sha256_256s_simple + sphincs\shake256_128f_robust @@ -1500,6 +1824,42 @@ {40b4c623-511f-4235-be9b-9ff675457fe5} + + {f569c3b0-c6ba-4157-a858-a5fc52d51515} + + + {1be5ba25-e61c-4218-a949-129b3422d6d8} + + + {c2b7b42f-bebf-4a92-8f97-e12740a2f287} + + + {b380048d-601c-46d6-acb8-591a2c4e1854} + + + {639a3ad2-1d05-445a-b9ea-9112fe8a6e7d} + + + {eea572d4-7c0a-4d63-bd4d-5d038223a922} + + + {1972ed56-876a-4eca-aadd-91886adfe290} + + + {10d0a3dd-b2b8-4e74-b59c-dc94ea84b7c1} + + + {945476fd-8435-4df8-9b4b-c04e74aa6d03} + + + {d70d780f-3ff0-4146-a6ce-8801bc157f83} + + + {fd789855-13bb-40e9-a09f-9086dd62ab93} + + + {4823fa89-43de-45b0-ac13-3de5345ce48b} + {cf170e69-4718-4efd-a7ba-2388a826193c} diff --git a/VisualStudio/winconfig.h b/VisualStudio/winconfig.h index 250606bb8..5a7c7aba0 100644 --- a/VisualStudio/winconfig.h +++ b/VisualStudio/winconfig.h @@ -57,6 +57,18 @@ #define OQS_ENABLE_SIG_sphincs_haraka_256f_simple #define OQS_ENABLE_SIG_sphincs_haraka_256s_robust #define OQS_ENABLE_SIG_sphincs_haraka_256s_simple +#define OQS_ENABLE_SIG_sphincs_sha256_128f_robust +#define OQS_ENABLE_SIG_sphincs_sha256_128f_simple +#define OQS_ENABLE_SIG_sphincs_sha256_128s_robust +#define OQS_ENABLE_SIG_sphincs_sha256_128s_simple +#define OQS_ENABLE_SIG_sphincs_sha256_192f_robust +#define OQS_ENABLE_SIG_sphincs_sha256_192f_simple +#define OQS_ENABLE_SIG_sphincs_sha256_192s_robust +#define OQS_ENABLE_SIG_sphincs_sha256_192s_simple +#define OQS_ENABLE_SIG_sphincs_sha256_256f_robust +#define OQS_ENABLE_SIG_sphincs_sha256_256f_simple +#define OQS_ENABLE_SIG_sphincs_sha256_256s_robust +#define OQS_ENABLE_SIG_sphincs_sha256_256s_simple #define OQS_ENABLE_SIG_sphincs_shake256_128f_robust #define OQS_ENABLE_SIG_sphincs_shake256_128f_simple #define OQS_ENABLE_SIG_sphincs_shake256_128s_robust diff --git a/config/features.m4 b/config/features.m4 index 9769262eb..d6ff7bacb 100644 --- a/config/features.m4 +++ b/config/features.m4 @@ -120,6 +120,18 @@ AC_DEFUN([CONFIG_FEATURES], AC_DEFINE(OQS_ENABLE_SIG_sphincs_haraka_256f_simple, 1, "Define to 1 when SHPINCS+-Haraka-256f-simple enabled") AC_DEFINE(OQS_ENABLE_SIG_sphincs_haraka_256s_robust, 1, "Define to 1 when SHPINCS+-Haraka-256s-robust enabled") AC_DEFINE(OQS_ENABLE_SIG_sphincs_haraka_256s_simple, 1, "Define to 1 when SHPINCS+-Haraka-256s-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_128f_robust, 1, "Define to 1 when SHPINCS+-SHA256-128f-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_128f_simple, 1, "Define to 1 when SHPINCS+-SHA256-128f-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_128s_robust, 1, "Define to 1 when SHPINCS+-SHA256-128s-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_128s_simple, 1, "Define to 1 when SHPINCS+-SHA256-128s-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_192f_robust, 1, "Define to 1 when SHPINCS+-SHA256-192f-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_192f_simple, 1, "Define to 1 when SHPINCS+-SHA256-192f-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_192s_robust, 1, "Define to 1 when SHPINCS+-SHA256-192s-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_192s_simple, 1, "Define to 1 when SHPINCS+-SHA256-192s-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_256f_robust, 1, "Define to 1 when SHPINCS+-SHA256-256f-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_256f_simple, 1, "Define to 1 when SHPINCS+-SHA256-256f-simple enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_256s_robust, 1, "Define to 1 when SHPINCS+-SHA256-256s-robust enabled") + AC_DEFINE(OQS_ENABLE_SIG_sphincs_sha256_256s_simple, 1, "Define to 1 when SHPINCS+-SHA256-256s-simple enabled") AC_DEFINE(OQS_ENABLE_SIG_sphincs_shake256_128f_robust, 1, "Define to 1 when SHPINCS+-SHAKE256-128f-robust enabled") AC_DEFINE(OQS_ENABLE_SIG_sphincs_shake256_128f_simple, 1, "Define to 1 when SHPINCS+-SHAKE256-128f-simple enabled") AC_DEFINE(OQS_ENABLE_SIG_sphincs_shake256_128s_robust, 1, "Define to 1 when SHPINCS+-SHAKE256-128s-robust enabled") diff --git a/scripts/copy_from_pqclean/copy_from_pqclean.yml b/scripts/copy_from_pqclean/copy_from_pqclean.yml index c573e9b53..24cef67c4 100644 --- a/scripts/copy_from_pqclean/copy_from_pqclean.yml +++ b/scripts/copy_from_pqclean/copy_from_pqclean.yml @@ -231,6 +231,90 @@ sigs: implementation: clean sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_haraka.c', 'thash_haraka_simple.c', 'haraka.c'] visual_studio_guid: 40b4c623-511f-4235-be9b-9ff675457fe5 + - + scheme: sha256_128f_robust + pqclean_scheme: sphincs-sha256-128f-robust + pretty_name_full: SHPINCS+-SHA256-128f-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: f569c3b0-c6ba-4157-a858-a5fc52d51515 + - + scheme: sha256_128f_simple + pqclean_scheme: sphincs-sha256-128f-simple + pretty_name_full: SHPINCS+-SHA256-128f-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: 1be5ba25-e61c-4218-a949-129b3422d6d8 + - + scheme: sha256_128s_robust + pqclean_scheme: sphincs-sha256-128s-robust + pretty_name_full: SHPINCS+-SHA256-128s-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: c2b7b42f-bebf-4a92-8f97-e12740a2f287 + - + scheme: sha256_128s_simple + pqclean_scheme: sphincs-sha256-128s-simple + pretty_name_full: SHPINCS+-SHA256-128s-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: b380048d-601c-46d6-acb8-591a2c4e1854 + - + scheme: sha256_192f_robust + pqclean_scheme: sphincs-sha256-192f-robust + pretty_name_full: SHPINCS+-SHA256-192f-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: 639a3ad2-1d05-445a-b9ea-9112fe8a6e7d + - + scheme: sha256_192f_simple + pqclean_scheme: sphincs-sha256-192f-simple + pretty_name_full: SHPINCS+-SHA256-192f-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: eea572d4-7c0a-4d63-bd4d-5d038223a922 + - + scheme: sha256_192s_robust + pqclean_scheme: sphincs-sha256-192s-robust + pretty_name_full: SHPINCS+-SHA256-192s-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: 1972ed56-876a-4eca-aadd-91886adfe290 + - + scheme: sha256_192s_simple + pqclean_scheme: sphincs-sha256-192s-simple + pretty_name_full: SHPINCS+-SHA256-192s-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: 10d0a3dd-b2b8-4e74-b59c-dc94ea84b7c1 + - + scheme: sha256_256f_robust + pqclean_scheme: sphincs-sha256-256f-robust + pretty_name_full: SHPINCS+-SHA256-256f-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: 945476fd-8435-4df8-9b4b-c04e74aa6d03 + - + scheme: sha256_256f_simple + pqclean_scheme: sphincs-sha256-256f-simple + pretty_name_full: SHPINCS+-SHA256-256f-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: d70d780f-3ff0-4146-a6ce-8801bc157f83 + - + scheme: sha256_256s_robust + pqclean_scheme: sphincs-sha256-256s-robust + pretty_name_full: SHPINCS+-SHA256-256s-robust + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_robust.c', 'sha256.c'] + visual_studio_guid: fd789855-13bb-40e9-a09f-9086dd62ab93 + - + scheme: sha256_256s_simple + pqclean_scheme: sphincs-sha256-256s-simple + pretty_name_full: SHPINCS+-SHA256-256s-simple + implementation: clean + sources: ['address.c', 'wots.c', 'utils.c', 'fors.c', 'sign.c', 'hash_sha256.c', 'thash_sha256_simple.c', 'sha256.c'] + visual_studio_guid: 4823fa89-43de-45b0-ac13-3de5345ce48b - scheme: shake256_128f_robust pqclean_scheme: sphincs-shake256-128f-robust diff --git a/src/sig/sig.c b/src/sig/sig.c index a98da979e..d4d3d2259 100644 --- a/src/sig/sig.c +++ b/src/sig/sig.c @@ -15,7 +15,7 @@ OQS_API const char *OQS_SIG_alg_identifier(size_t i) { ///// OQS_COPY_FROM_PQCLEAN_FRAGMENT_ALG_IDENTIFIER_START OQS_SIG_alg_dilithium_2, OQS_SIG_alg_dilithium_3, OQS_SIG_alg_dilithium_4, OQS_SIG_alg_mqdss_31_48, OQS_SIG_alg_mqdss_31_64, - OQS_SIG_alg_sphincs_haraka_128f_robust, OQS_SIG_alg_sphincs_haraka_128f_simple, OQS_SIG_alg_sphincs_haraka_128s_robust, OQS_SIG_alg_sphincs_haraka_128s_simple, OQS_SIG_alg_sphincs_haraka_192f_robust, OQS_SIG_alg_sphincs_haraka_192f_simple, OQS_SIG_alg_sphincs_haraka_192s_robust, OQS_SIG_alg_sphincs_haraka_192s_simple, OQS_SIG_alg_sphincs_haraka_256f_robust, OQS_SIG_alg_sphincs_haraka_256f_simple, OQS_SIG_alg_sphincs_haraka_256s_robust, OQS_SIG_alg_sphincs_haraka_256s_simple, OQS_SIG_alg_sphincs_shake256_128f_robust, OQS_SIG_alg_sphincs_shake256_128f_simple, OQS_SIG_alg_sphincs_shake256_128s_robust, OQS_SIG_alg_sphincs_shake256_128s_simple, OQS_SIG_alg_sphincs_shake256_192f_robust, OQS_SIG_alg_sphincs_shake256_192f_simple, OQS_SIG_alg_sphincs_shake256_192s_robust, OQS_SIG_alg_sphincs_shake256_192s_simple, OQS_SIG_alg_sphincs_shake256_256f_robust, OQS_SIG_alg_sphincs_shake256_256f_simple, OQS_SIG_alg_sphincs_shake256_256s_robust, OQS_SIG_alg_sphincs_shake256_256s_simple, + OQS_SIG_alg_sphincs_haraka_128f_robust, OQS_SIG_alg_sphincs_haraka_128f_simple, OQS_SIG_alg_sphincs_haraka_128s_robust, OQS_SIG_alg_sphincs_haraka_128s_simple, OQS_SIG_alg_sphincs_haraka_192f_robust, OQS_SIG_alg_sphincs_haraka_192f_simple, OQS_SIG_alg_sphincs_haraka_192s_robust, OQS_SIG_alg_sphincs_haraka_192s_simple, OQS_SIG_alg_sphincs_haraka_256f_robust, OQS_SIG_alg_sphincs_haraka_256f_simple, OQS_SIG_alg_sphincs_haraka_256s_robust, OQS_SIG_alg_sphincs_haraka_256s_simple, OQS_SIG_alg_sphincs_sha256_128f_robust, OQS_SIG_alg_sphincs_sha256_128f_simple, OQS_SIG_alg_sphincs_sha256_128s_robust, OQS_SIG_alg_sphincs_sha256_128s_simple, OQS_SIG_alg_sphincs_sha256_192f_robust, OQS_SIG_alg_sphincs_sha256_192f_simple, OQS_SIG_alg_sphincs_sha256_192s_robust, OQS_SIG_alg_sphincs_sha256_192s_simple, OQS_SIG_alg_sphincs_sha256_256f_robust, OQS_SIG_alg_sphincs_sha256_256f_simple, OQS_SIG_alg_sphincs_sha256_256s_robust, OQS_SIG_alg_sphincs_sha256_256s_simple, OQS_SIG_alg_sphincs_shake256_128f_robust, OQS_SIG_alg_sphincs_shake256_128f_simple, OQS_SIG_alg_sphincs_shake256_128s_robust, OQS_SIG_alg_sphincs_shake256_128s_simple, OQS_SIG_alg_sphincs_shake256_192f_robust, OQS_SIG_alg_sphincs_shake256_192f_simple, OQS_SIG_alg_sphincs_shake256_192s_robust, OQS_SIG_alg_sphincs_shake256_192s_simple, OQS_SIG_alg_sphincs_shake256_256f_robust, OQS_SIG_alg_sphincs_shake256_256f_simple, OQS_SIG_alg_sphincs_shake256_256s_robust, OQS_SIG_alg_sphincs_shake256_256s_simple, ///// OQS_COPY_FROM_PQCLEAN_FRAGMENT_ALG_IDENTIFIER_END OQS_SIG_alg_picnic_L1_FS, OQS_SIG_alg_picnic_L1_UR, OQS_SIG_alg_picnic_L3_FS, OQS_SIG_alg_picnic_L3_UR, OQS_SIG_alg_picnic_L5_FS, OQS_SIG_alg_picnic_L5_UR, OQS_SIG_alg_picnic2_L1_FS, OQS_SIG_alg_picnic2_L3_FS, OQS_SIG_alg_picnic2_L5_FS, OQS_SIG_alg_qTESLA_I, OQS_SIG_alg_qTESLA_III_size, OQS_SIG_alg_qTESLA_III_speed}; @@ -138,6 +138,78 @@ OQS_API OQS_SIG *OQS_SIG_new(const char *method_name) { return OQS_SIG_sphincs_haraka_256s_simple_new(); #else return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_128f_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_robust + return OQS_SIG_sphincs_sha256_128f_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_128f_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_simple + return OQS_SIG_sphincs_sha256_128f_simple_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_128s_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_robust + return OQS_SIG_sphincs_sha256_128s_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_128s_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_simple + return OQS_SIG_sphincs_sha256_128s_simple_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_192f_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_robust + return OQS_SIG_sphincs_sha256_192f_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_192f_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_simple + return OQS_SIG_sphincs_sha256_192f_simple_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_192s_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_robust + return OQS_SIG_sphincs_sha256_192s_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_192s_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_simple + return OQS_SIG_sphincs_sha256_192s_simple_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_256f_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_robust + return OQS_SIG_sphincs_sha256_256f_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_256f_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_simple + return OQS_SIG_sphincs_sha256_256f_simple_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_256s_robust)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_robust + return OQS_SIG_sphincs_sha256_256s_robust_new(); +#else + return NULL; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_sha256_256s_simple)) { +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_simple + return OQS_SIG_sphincs_sha256_256s_simple_new(); +#else + return NULL; #endif } else if (0 == strcasecmp(method_name, OQS_SIG_alg_sphincs_shake256_128f_robust)) { #ifdef OQS_ENABLE_SIG_sphincs_shake256_128f_robust diff --git a/src/sig/sig.h b/src/sig/sig.h index b2d7d355d..3a8516f55 100644 --- a/src/sig/sig.h +++ b/src/sig/sig.h @@ -90,6 +90,30 @@ extern "C" { #define OQS_SIG_alg_sphincs_haraka_256s_robust "SHPINCS+-Haraka-256s-robust" /** Algorithm identifier for SHPINCS+-Haraka-256s-simple */ #define OQS_SIG_alg_sphincs_haraka_256s_simple "SHPINCS+-Haraka-256s-simple" +/** Algorithm identifier for SHPINCS+-SHA256-128f-robust */ +#define OQS_SIG_alg_sphincs_sha256_128f_robust "SHPINCS+-SHA256-128f-robust" +/** Algorithm identifier for SHPINCS+-SHA256-128f-simple */ +#define OQS_SIG_alg_sphincs_sha256_128f_simple "SHPINCS+-SHA256-128f-simple" +/** Algorithm identifier for SHPINCS+-SHA256-128s-robust */ +#define OQS_SIG_alg_sphincs_sha256_128s_robust "SHPINCS+-SHA256-128s-robust" +/** Algorithm identifier for SHPINCS+-SHA256-128s-simple */ +#define OQS_SIG_alg_sphincs_sha256_128s_simple "SHPINCS+-SHA256-128s-simple" +/** Algorithm identifier for SHPINCS+-SHA256-192f-robust */ +#define OQS_SIG_alg_sphincs_sha256_192f_robust "SHPINCS+-SHA256-192f-robust" +/** Algorithm identifier for SHPINCS+-SHA256-192f-simple */ +#define OQS_SIG_alg_sphincs_sha256_192f_simple "SHPINCS+-SHA256-192f-simple" +/** Algorithm identifier for SHPINCS+-SHA256-192s-robust */ +#define OQS_SIG_alg_sphincs_sha256_192s_robust "SHPINCS+-SHA256-192s-robust" +/** Algorithm identifier for SHPINCS+-SHA256-192s-simple */ +#define OQS_SIG_alg_sphincs_sha256_192s_simple "SHPINCS+-SHA256-192s-simple" +/** Algorithm identifier for SHPINCS+-SHA256-256f-robust */ +#define OQS_SIG_alg_sphincs_sha256_256f_robust "SHPINCS+-SHA256-256f-robust" +/** Algorithm identifier for SHPINCS+-SHA256-256f-simple */ +#define OQS_SIG_alg_sphincs_sha256_256f_simple "SHPINCS+-SHA256-256f-simple" +/** Algorithm identifier for SHPINCS+-SHA256-256s-robust */ +#define OQS_SIG_alg_sphincs_sha256_256s_robust "SHPINCS+-SHA256-256s-robust" +/** Algorithm identifier for SHPINCS+-SHA256-256s-simple */ +#define OQS_SIG_alg_sphincs_sha256_256s_simple "SHPINCS+-SHA256-256s-simple" /** Algorithm identifier for SHPINCS+-SHAKE256-128f-robust */ #define OQS_SIG_alg_sphincs_shake256_128f_robust "SHPINCS+-SHAKE256-128f-robust" /** Algorithm identifier for SHPINCS+-SHAKE256-128f-simple */ @@ -118,7 +142,7 @@ extern "C" { // EDIT-WHEN-ADDING-SIG ///// OQS_COPY_FROM_PQCLEAN_FRAGMENT_ALGS_LENGTH_START /** Number of algorithm identifiers above (including default). */ -#define OQS_SIG_algs_length 42 +#define OQS_SIG_algs_length 54 ///// OQS_COPY_FROM_PQCLEAN_FRAGMENT_ALGS_LENGTH_END /** diff --git a/src/sig/sphincs/Makefile.am b/src/sig/sphincs/Makefile.am index da0d6c50f..940b13c78 100644 --- a/src/sig/sphincs/Makefile.am +++ b/src/sig/sphincs/Makefile.am @@ -1,8 +1,8 @@ AUTOMAKE_OPTIONS = foreign noinst_LTLIBRARIES = libsigsphincs.la -noinst_LTLIBRARIES += libsigsphincs_haraka_128f_robust.la libsigsphincs_haraka_128f_simple.la libsigsphincs_haraka_128s_robust.la libsigsphincs_haraka_128s_simple.la libsigsphincs_haraka_192f_robust.la libsigsphincs_haraka_192f_simple.la libsigsphincs_haraka_192s_robust.la libsigsphincs_haraka_192s_simple.la libsigsphincs_haraka_256f_robust.la libsigsphincs_haraka_256f_simple.la libsigsphincs_haraka_256s_robust.la libsigsphincs_haraka_256s_simple.la libsigsphincs_shake256_128f_robust.la libsigsphincs_shake256_128f_simple.la libsigsphincs_shake256_128s_robust.la libsigsphincs_shake256_128s_simple.la libsigsphincs_shake256_192f_robust.la libsigsphincs_shake256_192f_simple.la libsigsphincs_shake256_192s_robust.la libsigsphincs_shake256_192s_simple.la libsigsphincs_shake256_256f_robust.la libsigsphincs_shake256_256f_simple.la libsigsphincs_shake256_256s_robust.la libsigsphincs_shake256_256s_simple.la +noinst_LTLIBRARIES += libsigsphincs_haraka_128f_robust.la libsigsphincs_haraka_128f_simple.la libsigsphincs_haraka_128s_robust.la libsigsphincs_haraka_128s_simple.la libsigsphincs_haraka_192f_robust.la libsigsphincs_haraka_192f_simple.la libsigsphincs_haraka_192s_robust.la libsigsphincs_haraka_192s_simple.la libsigsphincs_haraka_256f_robust.la libsigsphincs_haraka_256f_simple.la libsigsphincs_haraka_256s_robust.la libsigsphincs_haraka_256s_simple.la libsigsphincs_sha256_128f_robust.la libsigsphincs_sha256_128f_simple.la libsigsphincs_sha256_128s_robust.la libsigsphincs_sha256_128s_simple.la libsigsphincs_sha256_192f_robust.la libsigsphincs_sha256_192f_simple.la libsigsphincs_sha256_192s_robust.la libsigsphincs_sha256_192s_simple.la libsigsphincs_sha256_256f_robust.la libsigsphincs_sha256_256f_simple.la libsigsphincs_sha256_256s_robust.la libsigsphincs_sha256_256s_simple.la libsigsphincs_shake256_128f_robust.la libsigsphincs_shake256_128f_simple.la libsigsphincs_shake256_128s_robust.la libsigsphincs_shake256_128s_simple.la libsigsphincs_shake256_192f_robust.la libsigsphincs_shake256_192f_simple.la libsigsphincs_shake256_192s_robust.la libsigsphincs_shake256_192s_simple.la libsigsphincs_shake256_256f_robust.la libsigsphincs_shake256_256f_simple.la libsigsphincs_shake256_256s_robust.la libsigsphincs_shake256_256s_simple.la -libsigsphincs_la_LIBADD = libsigsphincs_haraka_128f_robust.la libsigsphincs_haraka_128f_simple.la libsigsphincs_haraka_128s_robust.la libsigsphincs_haraka_128s_simple.la libsigsphincs_haraka_192f_robust.la libsigsphincs_haraka_192f_simple.la libsigsphincs_haraka_192s_robust.la libsigsphincs_haraka_192s_simple.la libsigsphincs_haraka_256f_robust.la libsigsphincs_haraka_256f_simple.la libsigsphincs_haraka_256s_robust.la libsigsphincs_haraka_256s_simple.la libsigsphincs_shake256_128f_robust.la libsigsphincs_shake256_128f_simple.la libsigsphincs_shake256_128s_robust.la libsigsphincs_shake256_128s_simple.la libsigsphincs_shake256_192f_robust.la libsigsphincs_shake256_192f_simple.la libsigsphincs_shake256_192s_robust.la libsigsphincs_shake256_192s_simple.la libsigsphincs_shake256_256f_robust.la libsigsphincs_shake256_256f_simple.la libsigsphincs_shake256_256s_robust.la libsigsphincs_shake256_256s_simple.la +libsigsphincs_la_LIBADD = libsigsphincs_haraka_128f_robust.la libsigsphincs_haraka_128f_simple.la libsigsphincs_haraka_128s_robust.la libsigsphincs_haraka_128s_simple.la libsigsphincs_haraka_192f_robust.la libsigsphincs_haraka_192f_simple.la libsigsphincs_haraka_192s_robust.la libsigsphincs_haraka_192s_simple.la libsigsphincs_haraka_256f_robust.la libsigsphincs_haraka_256f_simple.la libsigsphincs_haraka_256s_robust.la libsigsphincs_haraka_256s_simple.la libsigsphincs_sha256_128f_robust.la libsigsphincs_sha256_128f_simple.la libsigsphincs_sha256_128s_robust.la libsigsphincs_sha256_128s_simple.la libsigsphincs_sha256_192f_robust.la libsigsphincs_sha256_192f_simple.la libsigsphincs_sha256_192s_robust.la libsigsphincs_sha256_192s_simple.la libsigsphincs_sha256_256f_robust.la libsigsphincs_sha256_256f_simple.la libsigsphincs_sha256_256s_robust.la libsigsphincs_sha256_256s_simple.la libsigsphincs_shake256_128f_robust.la libsigsphincs_shake256_128f_simple.la libsigsphincs_shake256_128s_robust.la libsigsphincs_shake256_128s_simple.la libsigsphincs_shake256_192f_robust.la libsigsphincs_shake256_192f_simple.la libsigsphincs_shake256_192s_robust.la libsigsphincs_shake256_192s_simple.la libsigsphincs_shake256_256f_robust.la libsigsphincs_shake256_256f_simple.la libsigsphincs_shake256_256s_robust.la libsigsphincs_shake256_256s_simple.la libsigsphincs_la_SOURCES = libsigsphincs_haraka_128f_robust_la_SOURCES = sig_sphincs_haraka_128f_robust.c pqclean_sphincs-haraka-128f-robust_clean/address.c pqclean_sphincs-haraka-128f-robust_clean/wots.c pqclean_sphincs-haraka-128f-robust_clean/utils.c pqclean_sphincs-haraka-128f-robust_clean/fors.c pqclean_sphincs-haraka-128f-robust_clean/sign.c pqclean_sphincs-haraka-128f-robust_clean/hash_haraka.c pqclean_sphincs-haraka-128f-robust_clean/thash_haraka_robust.c pqclean_sphincs-haraka-128f-robust_clean/haraka.c @@ -41,6 +41,42 @@ libsigsphincs_haraka_256s_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclea libsigsphincs_haraka_256s_simple_la_SOURCES = sig_sphincs_haraka_256s_simple.c pqclean_sphincs-haraka-256s-simple_clean/address.c pqclean_sphincs-haraka-256s-simple_clean/wots.c pqclean_sphincs-haraka-256s-simple_clean/utils.c pqclean_sphincs-haraka-256s-simple_clean/fors.c pqclean_sphincs-haraka-256s-simple_clean/sign.c pqclean_sphincs-haraka-256s-simple_clean/hash_haraka.c pqclean_sphincs-haraka-256s-simple_clean/thash_haraka_simple.c pqclean_sphincs-haraka-256s-simple_clean/haraka.c libsigsphincs_haraka_256s_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims +libsigsphincs_sha256_128f_robust_la_SOURCES = sig_sphincs_sha256_128f_robust.c pqclean_sphincs-sha256-128f-robust_clean/address.c pqclean_sphincs-sha256-128f-robust_clean/wots.c pqclean_sphincs-sha256-128f-robust_clean/utils.c pqclean_sphincs-sha256-128f-robust_clean/fors.c pqclean_sphincs-sha256-128f-robust_clean/sign.c pqclean_sphincs-sha256-128f-robust_clean/hash_sha256.c pqclean_sphincs-sha256-128f-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-128f-robust_clean/sha256.c +libsigsphincs_sha256_128f_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_128f_simple_la_SOURCES = sig_sphincs_sha256_128f_simple.c pqclean_sphincs-sha256-128f-simple_clean/address.c pqclean_sphincs-sha256-128f-simple_clean/wots.c pqclean_sphincs-sha256-128f-simple_clean/utils.c pqclean_sphincs-sha256-128f-simple_clean/fors.c pqclean_sphincs-sha256-128f-simple_clean/sign.c pqclean_sphincs-sha256-128f-simple_clean/hash_sha256.c pqclean_sphincs-sha256-128f-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-128f-simple_clean/sha256.c +libsigsphincs_sha256_128f_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_128s_robust_la_SOURCES = sig_sphincs_sha256_128s_robust.c pqclean_sphincs-sha256-128s-robust_clean/address.c pqclean_sphincs-sha256-128s-robust_clean/wots.c pqclean_sphincs-sha256-128s-robust_clean/utils.c pqclean_sphincs-sha256-128s-robust_clean/fors.c pqclean_sphincs-sha256-128s-robust_clean/sign.c pqclean_sphincs-sha256-128s-robust_clean/hash_sha256.c pqclean_sphincs-sha256-128s-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-128s-robust_clean/sha256.c +libsigsphincs_sha256_128s_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_128s_simple_la_SOURCES = sig_sphincs_sha256_128s_simple.c pqclean_sphincs-sha256-128s-simple_clean/address.c pqclean_sphincs-sha256-128s-simple_clean/wots.c pqclean_sphincs-sha256-128s-simple_clean/utils.c pqclean_sphincs-sha256-128s-simple_clean/fors.c pqclean_sphincs-sha256-128s-simple_clean/sign.c pqclean_sphincs-sha256-128s-simple_clean/hash_sha256.c pqclean_sphincs-sha256-128s-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-128s-simple_clean/sha256.c +libsigsphincs_sha256_128s_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_192f_robust_la_SOURCES = sig_sphincs_sha256_192f_robust.c pqclean_sphincs-sha256-192f-robust_clean/address.c pqclean_sphincs-sha256-192f-robust_clean/wots.c pqclean_sphincs-sha256-192f-robust_clean/utils.c pqclean_sphincs-sha256-192f-robust_clean/fors.c pqclean_sphincs-sha256-192f-robust_clean/sign.c pqclean_sphincs-sha256-192f-robust_clean/hash_sha256.c pqclean_sphincs-sha256-192f-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-192f-robust_clean/sha256.c +libsigsphincs_sha256_192f_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_192f_simple_la_SOURCES = sig_sphincs_sha256_192f_simple.c pqclean_sphincs-sha256-192f-simple_clean/address.c pqclean_sphincs-sha256-192f-simple_clean/wots.c pqclean_sphincs-sha256-192f-simple_clean/utils.c pqclean_sphincs-sha256-192f-simple_clean/fors.c pqclean_sphincs-sha256-192f-simple_clean/sign.c pqclean_sphincs-sha256-192f-simple_clean/hash_sha256.c pqclean_sphincs-sha256-192f-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-192f-simple_clean/sha256.c +libsigsphincs_sha256_192f_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_192s_robust_la_SOURCES = sig_sphincs_sha256_192s_robust.c pqclean_sphincs-sha256-192s-robust_clean/address.c pqclean_sphincs-sha256-192s-robust_clean/wots.c pqclean_sphincs-sha256-192s-robust_clean/utils.c pqclean_sphincs-sha256-192s-robust_clean/fors.c pqclean_sphincs-sha256-192s-robust_clean/sign.c pqclean_sphincs-sha256-192s-robust_clean/hash_sha256.c pqclean_sphincs-sha256-192s-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-192s-robust_clean/sha256.c +libsigsphincs_sha256_192s_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_192s_simple_la_SOURCES = sig_sphincs_sha256_192s_simple.c pqclean_sphincs-sha256-192s-simple_clean/address.c pqclean_sphincs-sha256-192s-simple_clean/wots.c pqclean_sphincs-sha256-192s-simple_clean/utils.c pqclean_sphincs-sha256-192s-simple_clean/fors.c pqclean_sphincs-sha256-192s-simple_clean/sign.c pqclean_sphincs-sha256-192s-simple_clean/hash_sha256.c pqclean_sphincs-sha256-192s-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-192s-simple_clean/sha256.c +libsigsphincs_sha256_192s_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_256f_robust_la_SOURCES = sig_sphincs_sha256_256f_robust.c pqclean_sphincs-sha256-256f-robust_clean/address.c pqclean_sphincs-sha256-256f-robust_clean/wots.c pqclean_sphincs-sha256-256f-robust_clean/utils.c pqclean_sphincs-sha256-256f-robust_clean/fors.c pqclean_sphincs-sha256-256f-robust_clean/sign.c pqclean_sphincs-sha256-256f-robust_clean/hash_sha256.c pqclean_sphincs-sha256-256f-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-256f-robust_clean/sha256.c +libsigsphincs_sha256_256f_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_256f_simple_la_SOURCES = sig_sphincs_sha256_256f_simple.c pqclean_sphincs-sha256-256f-simple_clean/address.c pqclean_sphincs-sha256-256f-simple_clean/wots.c pqclean_sphincs-sha256-256f-simple_clean/utils.c pqclean_sphincs-sha256-256f-simple_clean/fors.c pqclean_sphincs-sha256-256f-simple_clean/sign.c pqclean_sphincs-sha256-256f-simple_clean/hash_sha256.c pqclean_sphincs-sha256-256f-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-256f-simple_clean/sha256.c +libsigsphincs_sha256_256f_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_256s_robust_la_SOURCES = sig_sphincs_sha256_256s_robust.c pqclean_sphincs-sha256-256s-robust_clean/address.c pqclean_sphincs-sha256-256s-robust_clean/wots.c pqclean_sphincs-sha256-256s-robust_clean/utils.c pqclean_sphincs-sha256-256s-robust_clean/fors.c pqclean_sphincs-sha256-256s-robust_clean/sign.c pqclean_sphincs-sha256-256s-robust_clean/hash_sha256.c pqclean_sphincs-sha256-256s-robust_clean/thash_sha256_robust.c pqclean_sphincs-sha256-256s-robust_clean/sha256.c +libsigsphincs_sha256_256s_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + +libsigsphincs_sha256_256s_simple_la_SOURCES = sig_sphincs_sha256_256s_simple.c pqclean_sphincs-sha256-256s-simple_clean/address.c pqclean_sphincs-sha256-256s-simple_clean/wots.c pqclean_sphincs-sha256-256s-simple_clean/utils.c pqclean_sphincs-sha256-256s-simple_clean/fors.c pqclean_sphincs-sha256-256s-simple_clean/sign.c pqclean_sphincs-sha256-256s-simple_clean/hash_sha256.c pqclean_sphincs-sha256-256s-simple_clean/thash_sha256_simple.c pqclean_sphincs-sha256-256s-simple_clean/sha256.c +libsigsphincs_sha256_256s_simple_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims + libsigsphincs_shake256_128f_robust_la_SOURCES = sig_sphincs_shake256_128f_robust.c pqclean_sphincs-shake256-128f-robust_clean/address.c pqclean_sphincs-shake256-128f-robust_clean/wots.c pqclean_sphincs-shake256-128f-robust_clean/utils.c pqclean_sphincs-shake256-128f-robust_clean/fors.c pqclean_sphincs-shake256-128f-robust_clean/sign.c pqclean_sphincs-shake256-128f-robust_clean/hash_shake256.c pqclean_sphincs-shake256-128f-robust_clean/thash_shake256_robust.c libsigsphincs_shake256_128f_robust_la_CFLAGS = $(AM_CFLAGS) -I../../common/pqclean_shims diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.c new file mode 100644 index 000000000..eff46d7f4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.h new file mode 100644 index 000000000..2464391c4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/api.h new file mode 100644 index 000000000..2b084704d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.c new file mode 100644 index 000000000..502a164fa --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.h new file mode 100644 index 000000000..dab7d3ea7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash.h new file mode 100644 index 000000000..c5351f94e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_sha256.c new file mode 100644 index 000000000..15e0024c4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/params.h new file mode 100644 index 000000000..367ef88d0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.c new file mode 100644 index 000000000..f8e1ae1ed --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.h new file mode 100644 index 000000000..a1fb3d551 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sign.c new file mode 100644 index 000000000..aba2bece2 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash.h new file mode 100644 index 000000000..e6d3f9734 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..b07ae586f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.c new file mode 100644 index 000000000..78770ce55 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.h new file mode 100644 index 000000000..bcc82b9b3 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.c new file mode 100644 index 000000000..47aabc555 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.h new file mode 100644 index 000000000..e51fc73e0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.c new file mode 100644 index 000000000..6e5311ff8 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.h new file mode 100644 index 000000000..904e775e0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/api.h new file mode 100644 index 000000000..1b9b7cf40 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.c new file mode 100644 index 000000000..40c314fac --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.h new file mode 100644 index 000000000..f38dae6a9 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash.h new file mode 100644 index 000000000..ea25c8571 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_sha256.c new file mode 100644 index 000000000..36985dcff --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/params.h new file mode 100644 index 000000000..367ef88d0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.c new file mode 100644 index 000000000..d9303bb6c --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.h new file mode 100644 index 000000000..145ea57f6 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sign.c new file mode 100644 index 000000000..0af6ca791 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash.h new file mode 100644 index 000000000..35c873f54 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..137f4f7e5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.c new file mode 100644 index 000000000..f36d30042 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.h new file mode 100644 index 000000000..bec681575 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.c new file mode 100644 index 000000000..62c7c36b9 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.h new file mode 100644 index 000000000..33539db21 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128f-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.c new file mode 100644 index 000000000..5b946b756 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.h new file mode 100644 index 000000000..974c86f48 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/api.h new file mode 100644 index 000000000..1f339b06e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.c new file mode 100644 index 000000000..e92343bfd --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.h new file mode 100644 index 000000000..34193b875 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash.h new file mode 100644 index 000000000..7997b70e7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_sha256.c new file mode 100644 index 000000000..f69b211d2 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/params.h new file mode 100644 index 000000000..ac30fdea9 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.c new file mode 100644 index 000000000..fbaac27d6 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.h new file mode 100644 index 000000000..3e36added --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sign.c new file mode 100644 index 000000000..5acc1d6e5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash.h new file mode 100644 index 000000000..1ed87fc32 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..5716ee51f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.c new file mode 100644 index 000000000..b54d5737a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.h new file mode 100644 index 000000000..59e8f8990 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.c new file mode 100644 index 000000000..430103692 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.h new file mode 100644 index 000000000..1341368ce --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.c new file mode 100644 index 000000000..f58ec0ca0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.h new file mode 100644 index 000000000..cca73fa2b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/api.h new file mode 100644 index 000000000..2ccd88e6a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.c new file mode 100644 index 000000000..a6d22b867 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.h new file mode 100644 index 000000000..4be5f09b3 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash.h new file mode 100644 index 000000000..8f6be6b94 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_sha256.c new file mode 100644 index 000000000..4fd3dd474 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/params.h new file mode 100644 index 000000000..ac30fdea9 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.c new file mode 100644 index 000000000..f9821c72f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.h new file mode 100644 index 000000000..a6248d8a5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sign.c new file mode 100644 index 000000000..5e7e014cb --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash.h new file mode 100644 index 000000000..f0528a70b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..9a5f83710 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.c new file mode 100644 index 000000000..ac65026a9 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.h new file mode 100644 index 000000000..e3c5bf2a0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.c new file mode 100644 index 000000000..9f46cf0d0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.h new file mode 100644 index 000000000..8eaa20ee4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-128s-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.c new file mode 100644 index 000000000..220bf4a3d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.h new file mode 100644 index 000000000..4b69abb58 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/api.h new file mode 100644 index 000000000..5c25fb580 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.c new file mode 100644 index 000000000..5f06115fa --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.h new file mode 100644 index 000000000..db071c237 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash.h new file mode 100644 index 000000000..564125d6a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_sha256.c new file mode 100644 index 000000000..e93b7b890 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/params.h new file mode 100644 index 000000000..898f177f5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.c new file mode 100644 index 000000000..ff085bcff --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.h new file mode 100644 index 000000000..6723b5dec --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sign.c new file mode 100644 index 000000000..a952889c4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash.h new file mode 100644 index 000000000..62183b534 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..54d218bc1 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.c new file mode 100644 index 000000000..7e9555e99 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.h new file mode 100644 index 000000000..4fd7c3150 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.c new file mode 100644 index 000000000..c5e708307 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.h new file mode 100644 index 000000000..f2b729e6e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.c new file mode 100644 index 000000000..d268f11f0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.h new file mode 100644 index 000000000..b42f26728 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/api.h new file mode 100644 index 000000000..ffe81106a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.c new file mode 100644 index 000000000..237ddf934 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.h new file mode 100644 index 000000000..fa776f591 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash.h new file mode 100644 index 000000000..ecf8b897a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_sha256.c new file mode 100644 index 000000000..ce550d77a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/params.h new file mode 100644 index 000000000..898f177f5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.c new file mode 100644 index 000000000..64a46d5c2 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.h new file mode 100644 index 000000000..02c9ec010 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sign.c new file mode 100644 index 000000000..caf5fb31e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash.h new file mode 100644 index 000000000..2c33bac91 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..ad4a9f39d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.c new file mode 100644 index 000000000..c45d24c33 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.h new file mode 100644 index 000000000..8ddb883bd --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.c new file mode 100644 index 000000000..75c88fafa --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.h new file mode 100644 index 000000000..8c7de8fcc --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192f-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.c new file mode 100644 index 000000000..0da3ca4bc --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.h new file mode 100644 index 000000000..3ca030f1e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/api.h new file mode 100644 index 000000000..92fd25e65 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.c new file mode 100644 index 000000000..a9c5e714c --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.h new file mode 100644 index 000000000..1f089da06 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash.h new file mode 100644 index 000000000..d68519c24 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_sha256.c new file mode 100644 index 000000000..a888a8025 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/params.h new file mode 100644 index 000000000..483b5374c --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.c new file mode 100644 index 000000000..dee5d9b7b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.h new file mode 100644 index 000000000..ac466a260 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sign.c new file mode 100644 index 000000000..24ac5a6a3 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash.h new file mode 100644 index 000000000..0e7311837 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..043f17ee7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.c new file mode 100644 index 000000000..e677f6301 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.h new file mode 100644 index 000000000..abee6946b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.c new file mode 100644 index 000000000..ec7231c7f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.h new file mode 100644 index 000000000..082761b2b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.c new file mode 100644 index 000000000..6167103d8 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.h new file mode 100644 index 000000000..92441dafc --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/api.h new file mode 100644 index 000000000..34b7b06fc --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.c new file mode 100644 index 000000000..94796c110 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.h new file mode 100644 index 000000000..df82ee2eb --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash.h new file mode 100644 index 000000000..4cdf1b556 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_sha256.c new file mode 100644 index 000000000..b09ba8a41 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/params.h new file mode 100644 index 000000000..483b5374c --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.c new file mode 100644 index 000000000..246ccac6f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.h new file mode 100644 index 000000000..8e7eca6fd --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sign.c new file mode 100644 index 000000000..6fed698d7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash.h new file mode 100644 index 000000000..32ed14fdb --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..7e150cc60 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.c new file mode 100644 index 000000000..9900cdd91 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.h new file mode 100644 index 000000000..108951d0d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.c new file mode 100644 index 000000000..640e773da --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.h new file mode 100644 index 000000000..db6dd9273 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-192s-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.c new file mode 100644 index 000000000..8bae3cf37 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.h new file mode 100644 index 000000000..2a277cc11 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/api.h new file mode 100644 index 000000000..004aabe26 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.c new file mode 100644 index 000000000..a77f9873d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.h new file mode 100644 index 000000000..8cab02c03 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash.h new file mode 100644 index 000000000..feeaf282a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_sha256.c new file mode 100644 index 000000000..8c427f81b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/params.h new file mode 100644 index 000000000..c2ad0531f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.c new file mode 100644 index 000000000..f4ce32bbf --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.h new file mode 100644 index 000000000..1d72a22a8 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sign.c new file mode 100644 index 000000000..8b7ffc9c4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash.h new file mode 100644 index 000000000..dc5955c28 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..9cbd681c4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.c new file mode 100644 index 000000000..b9e9bc773 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.h new file mode 100644 index 000000000..4eb006788 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.c new file mode 100644 index 000000000..f29a16b82 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.h new file mode 100644 index 000000000..1b9f2bf99 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.c new file mode 100644 index 000000000..29f4ff701 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.h new file mode 100644 index 000000000..ee6e9cd9b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/api.h new file mode 100644 index 000000000..c40d207f3 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.c new file mode 100644 index 000000000..65b301f9b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.h new file mode 100644 index 000000000..7c8b8680a --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash.h new file mode 100644 index 000000000..9b438dc21 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_sha256.c new file mode 100644 index 000000000..a1af41bbe --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/params.h new file mode 100644 index 000000000..c2ad0531f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.c new file mode 100644 index 000000000..e75e740b3 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.h new file mode 100644 index 000000000..9babd89d5 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sign.c new file mode 100644 index 000000000..d3179951f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash.h new file mode 100644 index 000000000..3478f5284 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..9937ca105 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.c new file mode 100644 index 000000000..f13b05510 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.h new file mode 100644 index 000000000..747c021c1 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.c new file mode 100644 index 000000000..72a9d659d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.h new file mode 100644 index 000000000..7dc350deb --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256f-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.c new file mode 100644 index 000000000..fe9222930 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.h new file mode 100644 index 000000000..8144aec6b --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/api.h new file mode 100644 index 000000000..492ce2d54 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.c new file mode 100644 index 000000000..f6b100f86 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.h new file mode 100644 index 000000000..10d760648 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash.h new file mode 100644 index 000000000..8ec0b1d0f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_sha256.c new file mode 100644 index 000000000..6dcdad0b4 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/params.h new file mode 100644 index 000000000..f023d687e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.c new file mode 100644 index 000000000..7e2adb370 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.h new file mode 100644 index 000000000..73365a182 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sign.c new file mode 100644 index 000000000..2b2d669da --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash.h new file mode 100644 index 000000000..869b49d14 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash_sha256_robust.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash_sha256_robust.c new file mode 100644 index 000000000..010d58b04 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/thash_sha256_robust.c @@ -0,0 +1,82 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + sha256ctx sha2_state; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, &sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.c new file mode 100644 index 000000000..ea0c5851f --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.h new file mode 100644 index 000000000..e28aff94c --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.c new file mode 100644 index 000000000..e285fb838 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.h new file mode 100644 index 000000000..8c9ca9c6d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-robust_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/LICENSE b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/LICENSE new file mode 100644 index 000000000..670154e35 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.c new file mode 100644 index 000000000..67e83a3db --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.h new file mode 100644 index 000000000..cb27a5ef8 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/api.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/api.h new file mode 100644 index 000000000..72dff3639 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.c new file mode 100644 index 000000000..1971bca64 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8], const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8], + const hash_state *hash_state_seeded) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8], + const hash_state *hash_state_seeded) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr, hash_state_seeded); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr, hash_state_seeded); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr, + hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr, hash_state_seeded); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.h new file mode 100644 index 000000000..1f34467aa --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" +#include "hash_state.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8], const hash_state *hash_state_seeded); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash.h new file mode 100644 index 000000000..da9f5f3f7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash.h @@ -0,0 +1,29 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include "hash_state.h" + +#include +#include + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_sha256.c new file mode 100644 index 000000000..2bca40e56 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_sha256.c @@ -0,0 +1,157 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + hash_state *hash_state_seeded, + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(hash_state_seeded, pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen, const hash_state *hash_state_seeded) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + sha256ctx state; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(&state); + sha256_inc_blocks(&state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(&state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, &state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const hash_state *hash_state_seeded) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + sha256ctx state; + + sha256_inc_init(&state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, &state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(&state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, &state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); + + (void)hash_state_seeded; /* Prevent unused parameter warning. */ +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_state.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_state.h new file mode 100644 index 000000000..7b038e71d --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/hash_state.h @@ -0,0 +1,7 @@ +#ifndef SPX_PRIMITIVE_H +#define SPX_PRIMITIVE_H + +#include "sha2.h" +#define hash_state sha256ctx + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/params.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/params.h new file mode 100644 index 000000000..f023d687e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.c new file mode 100644 index 000000000..6ce718a49 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.c @@ -0,0 +1,71 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes hash_state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(hash_state_seeded); + sha256_inc_blocks(hash_state_seeded, block, 1); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.h new file mode 100644 index 000000000..49908e5f0 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +#include "sha2.h" + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(sha256ctx *hash_state_seeded, const unsigned char *pub_seed); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sign.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sign.c new file mode 100644 index 000000000..90d7a33b2 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/sign.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr, hash_state_seeded); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + hash_state hash_state_seeded; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function(&hash_state_seeded, pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr, &hash_state_seeded); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + hash_state hash_state_seeded; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen, &hash_state_seeded); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + hash_state hash_state_seeded; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + &hash_state_seeded, + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen, &hash_state_seeded); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr, &hash_state_seeded); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr, &hash_state_seeded); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr, &hash_state_seeded); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash.h new file mode 100644 index 000000000..326de9933 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash.h @@ -0,0 +1,27 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include +#include "hash_state.h" + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash_sha256_simple.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash_sha256_simple.c new file mode 100644 index 000000000..338b1d9b1 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/thash_sha256_simple.c @@ -0,0 +1,75 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + sha256ctx sha2_state; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + sha256_inc_dupe_state(&sha2_state, hash_state_seeded); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, &sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8], + const sha256ctx *hash_state_seeded) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.c new file mode 100644 index 000000000..c42156b85 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.c @@ -0,0 +1,199 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr, hash_state_seeded); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr, hash_state_seeded); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], + const hash_state *hash_state_seeded) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr, + hash_state_seeded); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr, hash_state_seeded); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr, hash_state_seeded); +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.h new file mode 100644 index 000000000..760f2e469 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/utils.h @@ -0,0 +1,64 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include "hash_state.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */, + const hash_state * /* hash_state_seeded */), + uint32_t tree_addr[8], const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.c b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.c new file mode 100644 index 000000000..c77d4d44e --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.c @@ -0,0 +1,167 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "hash_state.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8], + const hash_state *hash_state_seeded) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr, hash_state_seeded); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr, hash_state_seeded); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr, hash_state_seeded); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr, hash_state_seeded); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr, + hash_state_seeded); + } +} diff --git a/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.h b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.h new file mode 100644 index 000000000..99ffbe2c7 --- /dev/null +++ b/src/sig/sphincs/pqclean_sphincs-sha256-256s-simple_clean/wots.h @@ -0,0 +1,41 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include "hash_state.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8], const hash_state *hash_state_seeded); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8], + const hash_state *hash_state_seeded); + +#endif diff --git a/src/sig/sphincs/sig_sphincs.h b/src/sig/sphincs/sig_sphincs.h index 36d097bce..e1d999f83 100644 --- a/src/sig/sphincs/sig_sphincs.h +++ b/src/sig/sphincs/sig_sphincs.h @@ -135,6 +135,138 @@ OQS_API OQS_STATUS OQS_SIG_sphincs_haraka_256s_simple_sign(uint8_t *signature, s OQS_API OQS_STATUS OQS_SIG_sphincs_haraka_256s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); #endif +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_robust +#define OQS_SIG_sphincs_sha256_128f_robust_length_public_key 32 +#define OQS_SIG_sphincs_sha256_128f_robust_length_secret_key 64 +#define OQS_SIG_sphincs_sha256_128f_robust_length_signature 16976 + +OQS_SIG *OQS_SIG_sphincs_sha256_128f_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_simple +#define OQS_SIG_sphincs_sha256_128f_simple_length_public_key 32 +#define OQS_SIG_sphincs_sha256_128f_simple_length_secret_key 64 +#define OQS_SIG_sphincs_sha256_128f_simple_length_signature 16976 + +OQS_SIG *OQS_SIG_sphincs_sha256_128f_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_robust +#define OQS_SIG_sphincs_sha256_128s_robust_length_public_key 32 +#define OQS_SIG_sphincs_sha256_128s_robust_length_secret_key 64 +#define OQS_SIG_sphincs_sha256_128s_robust_length_signature 8080 + +OQS_SIG *OQS_SIG_sphincs_sha256_128s_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_simple +#define OQS_SIG_sphincs_sha256_128s_simple_length_public_key 32 +#define OQS_SIG_sphincs_sha256_128s_simple_length_secret_key 64 +#define OQS_SIG_sphincs_sha256_128s_simple_length_signature 8080 + +OQS_SIG *OQS_SIG_sphincs_sha256_128s_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_robust +#define OQS_SIG_sphincs_sha256_192f_robust_length_public_key 48 +#define OQS_SIG_sphincs_sha256_192f_robust_length_secret_key 96 +#define OQS_SIG_sphincs_sha256_192f_robust_length_signature 35664 + +OQS_SIG *OQS_SIG_sphincs_sha256_192f_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_simple +#define OQS_SIG_sphincs_sha256_192f_simple_length_public_key 48 +#define OQS_SIG_sphincs_sha256_192f_simple_length_secret_key 96 +#define OQS_SIG_sphincs_sha256_192f_simple_length_signature 35664 + +OQS_SIG *OQS_SIG_sphincs_sha256_192f_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_robust +#define OQS_SIG_sphincs_sha256_192s_robust_length_public_key 48 +#define OQS_SIG_sphincs_sha256_192s_robust_length_secret_key 96 +#define OQS_SIG_sphincs_sha256_192s_robust_length_signature 17064 + +OQS_SIG *OQS_SIG_sphincs_sha256_192s_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_simple +#define OQS_SIG_sphincs_sha256_192s_simple_length_public_key 48 +#define OQS_SIG_sphincs_sha256_192s_simple_length_secret_key 96 +#define OQS_SIG_sphincs_sha256_192s_simple_length_signature 17064 + +OQS_SIG *OQS_SIG_sphincs_sha256_192s_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_robust +#define OQS_SIG_sphincs_sha256_256f_robust_length_public_key 64 +#define OQS_SIG_sphincs_sha256_256f_robust_length_secret_key 128 +#define OQS_SIG_sphincs_sha256_256f_robust_length_signature 49216 + +OQS_SIG *OQS_SIG_sphincs_sha256_256f_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_simple +#define OQS_SIG_sphincs_sha256_256f_simple_length_public_key 64 +#define OQS_SIG_sphincs_sha256_256f_simple_length_secret_key 128 +#define OQS_SIG_sphincs_sha256_256f_simple_length_signature 49216 + +OQS_SIG *OQS_SIG_sphincs_sha256_256f_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_robust +#define OQS_SIG_sphincs_sha256_256s_robust_length_public_key 64 +#define OQS_SIG_sphincs_sha256_256s_robust_length_secret_key 128 +#define OQS_SIG_sphincs_sha256_256s_robust_length_signature 29792 + +OQS_SIG *OQS_SIG_sphincs_sha256_256s_robust_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_simple +#define OQS_SIG_sphincs_sha256_256s_simple_length_public_key 64 +#define OQS_SIG_sphincs_sha256_256s_simple_length_secret_key 128 +#define OQS_SIG_sphincs_sha256_256s_simple_length_signature 29792 + +OQS_SIG *OQS_SIG_sphincs_sha256_256s_simple_new(); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_keypair(uint8_t *public_key, uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key); +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); +#endif + #ifdef OQS_ENABLE_SIG_sphincs_shake256_128f_robust #define OQS_SIG_sphincs_shake256_128f_robust_length_public_key 32 #define OQS_SIG_sphincs_shake256_128f_robust_length_secret_key 64 diff --git a/src/sig/sphincs/sig_sphincs_sha256_128f_robust.c b/src/sig/sphincs/sig_sphincs_sha256_128f_robust.c new file mode 100644 index 000000000..d3d1c2ced --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_128f_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_128f_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_128f_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_128f_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_128f_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_128f_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_128f_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_128f_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_128f_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_128f_simple.c b/src/sig/sphincs/sig_sphincs_sha256_128f_simple.c new file mode 100644 index 000000000..e1adf4979 --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_128f_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128f_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_128f_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_128f_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_128f_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_128f_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_128f_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_128f_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_128f_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_128f_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_128s_robust.c b/src/sig/sphincs/sig_sphincs_sha256_128s_robust.c new file mode 100644 index 000000000..aaa83d89f --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_128s_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_128s_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_128s_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_128s_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_128s_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_128s_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_128s_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_128s_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_128s_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_128s_simple.c b/src/sig/sphincs/sig_sphincs_sha256_128s_simple.c new file mode 100644 index 000000000..f0d4c058f --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_128s_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_128s_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_128s_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_128s_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 1; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_128s_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_128s_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_128s_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_128s_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_128s_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_128s_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_128s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_192f_robust.c b/src/sig/sphincs/sig_sphincs_sha256_192f_robust.c new file mode 100644 index 000000000..7a9d59f57 --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_192f_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_192f_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_192f_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_192f_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_192f_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_192f_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_192f_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_192f_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_192f_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_192f_simple.c b/src/sig/sphincs/sig_sphincs_sha256_192f_simple.c new file mode 100644 index 000000000..b31f78d81 --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_192f_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192f_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_192f_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_192f_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_192f_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_192f_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_192f_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_192f_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_192f_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_192f_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_192s_robust.c b/src/sig/sphincs/sig_sphincs_sha256_192s_robust.c new file mode 100644 index 000000000..e2cbd22db --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_192s_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_192s_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_192s_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_192s_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_192s_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_192s_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_192s_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_192s_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_192s_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_192s_simple.c b/src/sig/sphincs/sig_sphincs_sha256_192s_simple.c new file mode 100644 index 000000000..e159e896f --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_192s_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_192s_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_192s_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_192s_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 3; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_192s_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_192s_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_192s_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_192s_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_192s_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_192s_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_192s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_256f_robust.c b/src/sig/sphincs/sig_sphincs_sha256_256f_robust.c new file mode 100644 index 000000000..760b02bfd --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_256f_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_256f_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_256f_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_256f_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_256f_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_256f_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_256f_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_256f_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_256f_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_256f_simple.c b/src/sig/sphincs/sig_sphincs_sha256_256f_simple.c new file mode 100644 index 000000000..999ce7a97 --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_256f_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256f_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_256f_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_256f_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_256f_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_256f_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_256f_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_256f_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_256f_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_256f_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256f_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_256s_robust.c b/src/sig/sphincs/sig_sphincs_sha256_256s_robust.c new file mode 100644 index 000000000..ba37449e8 --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_256s_robust.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_robust + +OQS_SIG *OQS_SIG_sphincs_sha256_256s_robust_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_256s_robust; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_256s_robust_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_256s_robust_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_256s_robust_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_256s_robust_keypair; + sig->sign = OQS_SIG_sphincs_sha256_256s_robust_sign; + sig->verify = OQS_SIG_sphincs_sha256_256s_robust_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_robust_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif diff --git a/src/sig/sphincs/sig_sphincs_sha256_256s_simple.c b/src/sig/sphincs/sig_sphincs_sha256_256s_simple.c new file mode 100644 index 000000000..6c31a28ba --- /dev/null +++ b/src/sig/sphincs/sig_sphincs_sha256_256s_simple.c @@ -0,0 +1,44 @@ +#include + +#include + +#ifdef OQS_ENABLE_SIG_sphincs_sha256_256s_simple + +OQS_SIG *OQS_SIG_sphincs_sha256_256s_simple_new() { + + OQS_SIG *sig = malloc(sizeof(OQS_SIG)); + if (sig == NULL) { + return NULL; + } + sig->method_name = OQS_SIG_alg_sphincs_sha256_256s_simple; + sig->alg_version = "https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441"; + + sig->claimed_nist_level = 5; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_sphincs_sha256_256s_simple_length_public_key; + sig->length_secret_key = OQS_SIG_sphincs_sha256_256s_simple_length_secret_key; + sig->length_signature = OQS_SIG_sphincs_sha256_256s_simple_length_signature; + + sig->keypair = OQS_SIG_sphincs_sha256_256s_simple_keypair; + sig->sign = OQS_SIG_sphincs_sha256_256s_simple_sign; + sig->verify = OQS_SIG_sphincs_sha256_256s_simple_verify; + + return sig; +} + +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); + +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_keypair(uint8_t *public_key, uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair(public_key, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, const uint8_t *secret_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len, secret_key); +} +OQS_API OQS_STATUS OQS_SIG_sphincs_sha256_256s_simple_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + return (OQS_STATUS) PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len, public_key); +} + +#endif